├── .circleci └── config.yml ├── .gitignore ├── .gitmodules ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── release.Makefile ├── src ├── Makefile ├── blink │ ├── Makefile │ ├── blink.c │ ├── stm32f103x8.ld │ └── uf2conv.py ├── bmp.sh ├── dapboot.c ├── dapboot.h ├── debug.cfg ├── dmesg.c ├── dmesg.h ├── dummy.c ├── gdb.sh ├── ghostfat.c ├── rules.mk ├── run.sh ├── stm32f103 │ ├── backup.c │ ├── backup.h │ ├── bluepill │ │ └── config.h │ ├── generic │ │ └── config.h │ ├── jacdac │ │ └── config.h │ ├── maplemini │ │ └── config.h │ ├── pxt32 │ │ └── config.h │ ├── stlink │ │ └── config.h │ ├── stm32f103x8.ld │ └── target_stm32f103.c ├── target.h ├── targets.mk ├── uf2.h ├── uf2cfg.h ├── usb_conf.c ├── usb_conf.h ├── usb_msc.c ├── webusb.c ├── webusb.h ├── webusb_defs.h ├── winusb.c ├── winusb.h └── winusb_defs.h └── util └── install-toolchain.sh /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | machine: true 5 | steps: 6 | - checkout 7 | - run: 8 | name: Checkout submodules 9 | command: git submodule sync && git submodule update --init 10 | - restore_cache: 11 | key: v1-toolchain-checksum-{{ checksum "util/install-toolchain.sh" }} 12 | - run: 13 | name: Install toolchain 14 | command: ./util/install-toolchain.sh 15 | - save_cache: 16 | key: v1-toolchain-checksum-{{ checksum "util/install-toolchain.sh" }} 17 | paths: 18 | - "~/toolchains/" 19 | - run: 20 | name: Compile firmware 21 | command: make -f release.Makefile -k all 22 | environment: 23 | PREFIX: "~/toolchains/gcc-arm-embedded/bin/arm-none-eabi" 24 | - store_artifacts: 25 | path: build/ 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore GNU global files 2 | GPATH 3 | GRTAGS 4 | GSYMS 5 | GTAGS 6 | 7 | # ignore build artifacts 8 | *.d 9 | *.o 10 | *.bin 11 | *.hex 12 | *.elf 13 | *.axf 14 | *.map 15 | *.uf2 16 | 17 | build 18 | 19 | # ignore local makefile settings 20 | src/local.mk 21 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libopencm3"] 2 | path = libopencm3 3 | url = https://github.com/devanlai/libopencm3.git 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "libopencm3/tests/gadget-zero/**/openocd.*.local.cfg": true, 4 | "libopencm3/**/*.d": true, 5 | "libopencm3/**/*.o": true, 6 | "libopencm3/**/*.bin": true, 7 | "libopencm3/**/*.hex": true, 8 | "libopencm3/**/*.list": true, 9 | "libopencm3/**/*.srec": true, 10 | "libopencm3/**/*.a": true, 11 | "libopencm3/**/*.elf": true, 12 | "libopencm3/lib/*.ld": true, 13 | "libopencm3/**/*.stylecheck": true, 14 | "libopencm3/**/*.swp": true, 15 | "libopencm3/**/\\#*": true, 16 | "libopencm3/**/.\\#*": true, 17 | "libopencm3/**/*~": true, 18 | "libopencm3/**/*.map": true, 19 | "libopencm3/**/*.log": true, 20 | "libopencm3/html/": true, 21 | "libopencm3/latex/": true, 22 | "libopencm3/**/*.pdf": true, 23 | "libopencm3/**/*.tag": true, 24 | "libopencm3/**/.DS_Store": true, 25 | "libopencm3/include/libopencm3/**/nvic.h": true, 26 | "libopencm3/include/libopencm3/**/**/nvic.h": true, 27 | "libopencm3/lib/**/vector_nvic.c": true, 28 | "libopencm3/lib/**/**/vector_nvic.c": true, 29 | "libopencm3/include/libopencmsis/efm32/": true, 30 | "libopencm3/include/libopencmsis/lm3s/": true, 31 | "libopencm3/include/libopencmsis/lpc13xx/": true, 32 | "libopencm3/include/libopencmsis/lpc17xx/": true, 33 | "libopencm3/include/libopencmsis/lpc43xx/": true, 34 | "libopencm3/include/libopencmsis/sam/": true, 35 | "libopencm3/include/libopencmsis/stm32/": true, 36 | "libopencm3/include/libopencmsis/vf6xx/": true, 37 | "libopencm3/nbproject/": true, 38 | "libopencm3/.idea/": true, 39 | "libopencm3/**/.project": true, 40 | "libopencm3/**/cscope.out": true, 41 | "**/GPATH": true, 42 | "**/GRTAGS": true, 43 | "**/GSYMS": true, 44 | "**/GTAGS": true, 45 | "**/*.d": true, 46 | "**/*.o": true, 47 | "**/*.bin": true, 48 | "**/*.hex": true, 49 | "**/*.elf": true, 50 | "**/*.axf": true, 51 | "**/*.map": true, 52 | "src/local.mk": true 53 | }, 54 | "files.associations": { 55 | "vector.h": "c", 56 | "cortex.h": "c", 57 | "rtc.h": "c" 58 | } 59 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | With the exception of files that contain licensing information 2 | stating the contrary, files within the dapboot project are licensed 3 | under the ISC license as follows: 4 | 5 | Copyright (c) 2016, Devan Lai 6 | 7 | Permission to use, copy, modify, and/or distribute this software 8 | for any purpose with or without fee is hereby granted, provided 9 | that the above copyright notice and this permission notice 10 | appear in all copies. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 14 | WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 15 | AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 16 | CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 17 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 18 | NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 19 | CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 | 21 | The following files in the dapboot project pertaining to the base 22 | Makefile and linker script are derived from the libopencm3 examples 23 | template and are licensed under the GNU Lesser General Public 24 | License version 3: 25 | * libopencm3.rules.mk 26 | * src/libopencm3.target.mk 27 | * src/stm32f042/stm32f042x6.ld 28 | * src/stm32f103/stm32f103x8.ld 29 | 30 | libopencm3, included as a submodule and statically linked as part 31 | of the build process, is separately licensed under the LGPLv3. 32 | 33 | The following is for informative purposes only: all LICENSE files 34 | contained within the project should be consulted for precise 35 | specifications. 36 | 37 | The LGLPv3 is the most restrictive license applying to any subset 38 | of the dapboot project; all other licensed material is strictly less 39 | restrictive than the LGPLv3. Accordingly, you may distribute the 40 | dapboot project in source or object code form under the terms of 41 | the LGPLv3 without needing to meet additional requirements for 42 | the less restrictively licensed portions of the dapboot project. 43 | 44 | Otherwise, the combined work consisting of the dapboot object code 45 | linked with the libopencm3 object code may be distributed without 46 | distributing a copy of the dapboot source code or any modifications 47 | to it. 48 | 49 | However, if the source code is not distributed, you must provide a 50 | means for the user to "re-link" the libopencm3 object code to 51 | produce a new combined work containing updated libopencm3 object 52 | code along-side any dapboot object code. 53 | 54 | The text of the GNU Lesser General Public License version 3 is 55 | reproduced below for use with the limited portions of the dapboot 56 | project licensed under the LGPLv3: 57 | 58 | GNU LESSER GENERAL PUBLIC LICENSE 59 | Version 3, 29 June 2007 60 | 61 | Copyright (C) 2007 Free Software Foundation, Inc. 62 | Everyone is permitted to copy and distribute verbatim copies 63 | of this license document, but changing it is not allowed. 64 | 65 | 66 | This version of the GNU Lesser General Public License incorporates 67 | the terms and conditions of version 3 of the GNU General Public 68 | License, supplemented by the additional permissions listed below. 69 | 70 | 0. Additional Definitions. 71 | 72 | As used herein, "this License" refers to version 3 of the GNU Lesser 73 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 74 | General Public License. 75 | 76 | "The Library" refers to a covered work governed by this License, 77 | other than an Application or a Combined Work as defined below. 78 | 79 | An "Application" is any work that makes use of an interface provided 80 | by the Library, but which is not otherwise based on the Library. 81 | Defining a subclass of a class defined by the Library is deemed a mode 82 | of using an interface provided by the Library. 83 | 84 | A "Combined Work" is a work produced by combining or linking an 85 | Application with the Library. The particular version of the Library 86 | with which the Combined Work was made is also called the "Linked 87 | Version". 88 | 89 | The "Minimal Corresponding Source" for a Combined Work means the 90 | Corresponding Source for the Combined Work, excluding any source code 91 | for portions of the Combined Work that, considered in isolation, are 92 | based on the Application, and not on the Linked Version. 93 | 94 | The "Corresponding Application Code" for a Combined Work means the 95 | object code and/or source code for the Application, including any data 96 | and utility programs needed for reproducing the Combined Work from the 97 | Application, but excluding the System Libraries of the Combined Work. 98 | 99 | 1. Exception to Section 3 of the GNU GPL. 100 | 101 | You may convey a covered work under sections 3 and 4 of this License 102 | without being bound by section 3 of the GNU GPL. 103 | 104 | 2. Conveying Modified Versions. 105 | 106 | If you modify a copy of the Library, and, in your modifications, a 107 | facility refers to a function or data to be supplied by an Application 108 | that uses the facility (other than as an argument passed when the 109 | facility is invoked), then you may convey a copy of the modified 110 | version: 111 | 112 | a) under this License, provided that you make a good faith effort to 113 | ensure that, in the event an Application does not supply the 114 | function or data, the facility still operates, and performs 115 | whatever part of its purpose remains meaningful, or 116 | 117 | b) under the GNU GPL, with none of the additional permissions of 118 | this License applicable to that copy. 119 | 120 | 3. Object Code Incorporating Material from Library Header Files. 121 | 122 | The object code form of an Application may incorporate material from 123 | a header file that is part of the Library. You may convey such object 124 | code under terms of your choice, provided that, if the incorporated 125 | material is not limited to numerical parameters, data structure 126 | layouts and accessors, or small macros, inline functions and templates 127 | (ten or fewer lines in length), you do both of the following: 128 | 129 | a) Give prominent notice with each copy of the object code that the 130 | Library is used in it and that the Library and its use are 131 | covered by this License. 132 | 133 | b) Accompany the object code with a copy of the GNU GPL and this license 134 | document. 135 | 136 | 4. Combined Works. 137 | 138 | You may convey a Combined Work under terms of your choice that, 139 | taken together, effectively do not restrict modification of the 140 | portions of the Library contained in the Combined Work and reverse 141 | engineering for debugging such modifications, if you also do each of 142 | the following: 143 | 144 | a) Give prominent notice with each copy of the Combined Work that 145 | the Library is used in it and that the Library and its use are 146 | covered by this License. 147 | 148 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 149 | document. 150 | 151 | c) For a Combined Work that displays copyright notices during 152 | execution, include the copyright notice for the Library among 153 | these notices, as well as a reference directing the user to the 154 | copies of the GNU GPL and this license document. 155 | 156 | d) Do one of the following: 157 | 158 | 0) Convey the Minimal Corresponding Source under the terms of this 159 | License, and the Corresponding Application Code in a form 160 | suitable for, and under terms that permit, the user to 161 | recombine or relink the Application with a modified version of 162 | the Linked Version to produce a modified Combined Work, in the 163 | manner specified by section 6 of the GNU GPL for conveying 164 | Corresponding Source. 165 | 166 | 1) Use a suitable shared library mechanism for linking with the 167 | Library. A suitable mechanism is one that (a) uses at run time 168 | a copy of the Library already present on the user's computer 169 | system, and (b) will operate properly with a modified version 170 | of the Library that is interface-compatible with the Linked 171 | Version. 172 | 173 | e) Provide Installation Information, but only if you would otherwise 174 | be required to provide such information under section 6 of the 175 | GNU GPL, and only to the extent that such information is 176 | necessary to install and execute a modified version of the 177 | Combined Work produced by recombining or relinking the 178 | Application with a modified version of the Linked Version. (If 179 | you use option 4d0, the Installation Information must accompany 180 | the Minimal Corresponding Source and Corresponding Application 181 | Code. If you use option 4d1, you must provide the Installation 182 | Information in the manner specified by section 6 of the GNU GPL 183 | for conveying Corresponding Source.) 184 | 185 | 5. Combined Libraries. 186 | 187 | You may place library facilities that are a work based on the 188 | Library side by side in a single library together with other library 189 | facilities that are not Applications and are not covered by this 190 | License, and convey such a combined library under terms of your 191 | choice, if you do both of the following: 192 | 193 | a) Accompany the combined library with a copy of the same work based 194 | on the Library, uncombined with any other library facilities, 195 | conveyed under the terms of this License. 196 | 197 | b) Give prominent notice with the combined library that part of it 198 | is a work based on the Library, and explaining where to find the 199 | accompanying uncombined form of the same work. 200 | 201 | 6. Revised Versions of the GNU Lesser General Public License. 202 | 203 | The Free Software Foundation may publish revised and/or new versions 204 | of the GNU Lesser General Public License from time to time. Such new 205 | versions will be similar in spirit to the present version, but may 206 | differ in detail to address new problems or concerns. 207 | 208 | Each version is given a distinguishing version number. If the 209 | Library as you received it specifies that a certain numbered version 210 | of the GNU Lesser General Public License "or any later version" 211 | applies to it, you have the option of following the terms and 212 | conditions either of that published version or of any later version 213 | published by the Free Software Foundation. If the Library as you 214 | received it does not specify a version number of the GNU Lesser 215 | General Public License, you may choose any version of the GNU Lesser 216 | General Public License ever published by the Free Software Foundation. 217 | 218 | If the Library as you received it specifies that a proxy can decide 219 | whether future versions of the GNU Lesser General Public License shall 220 | apply, that proxy's public statement of acceptance of any version is 221 | permanent authorization for you to choose that version for the 222 | Library. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UF2 bootloader for STM32F103 2 | 3 | This project was initially forked off https://github.com/devanlai/dapboot though the DFU functionality has been removed 4 | and replaced with a USB Mass Storage with [UF2 support](https://github.com/Microsoft/uf2) 5 | 6 | Boards with this bootloader can be programmed at https://maker.makecode.com 7 | using web interface using a graphical programming language or TypeScript (JavaScript with Types). 8 | 9 | ## Flashing bootloader from binaries 10 | 11 | You will need a STLink/v2 (or other debugger) to flash it. 12 | 13 | * https://github.com/mmoskal/uf2-stm32f103/releases 14 | * download the latest ZIP file (`uf2-stm32f103-vX.Y.Z.zip`) 15 | * run: `openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg -c "program uf2boot-BLUEPILL.bin verify reset exit 0x8000000"` 16 | * see if `BLUEPILL` drive appears; if not reset the board; the LED should be fading in and out about once per second 17 | 18 | ## Build instructions 19 | The default target is a generic STM32F103 dev board with an LED on PC13, commonly referred to as a "bluepill" board. 20 | 21 | To build other targets, you can override the 22 | `TARGET` variable when invoking `make`. 23 | 24 | make clean 25 | make TARGET=STLINK 26 | 27 | ### Targets 28 | 29 | | Target Name | Description | Link | 30 | | ----------- | ----------- |----- | 31 | |`BLUEPILL` | Cheap dev board | http://wiki.stm32duino.com/index.php?title=Blue_Pill | 32 | |`MAPLEMINI` | LeafLabs Maple Mini board and clone derivatives | http://wiki.stm32duino.com/index.php?title=Maple_Mini | 33 | |`STLINK` | STLink/v2 hardware clones | https://wiki.paparazziuav.org/wiki/STLink#Clones | 34 | | `PXT32` | MakeCode Arcade console (currently disabled on Arcade site) | https://arcade.makecode.com | 35 | 36 | 37 | ## Flash instructions 38 | 39 | The `make flash` target will use openocd to upload the bootloader to an attached board. 40 | By default, the Makefile assumes you're using a STLink v2 based probe, but you can override this by overriding `OOCD_INTERFACE` variable. For example: 41 | 42 | make OOCD_INTERFACE=interface/cmsis-dap.cfg flash 43 | 44 | ## Overriding defaults 45 | Local makefile settings can be set by creating a `local.mk`, which is automatically included. 46 | 47 | Here is an example `local.mk` that changes the default target to the STLink/v2 and uses an unmodified STLink/v2 to flash it. 48 | 49 | TARGET ?= STLINK 50 | OOCD_INTERFACE ?= interface/stlink-v2.cfg 51 | 52 | ## Using the bootloader 53 | ### Building for the bootloader 54 | The bootloader occupies the lower 16KiB of flash, so your application must offset its flash contents by 16KiB. This can be done by modifying your linker script or flags as appropriate. 55 | 56 | ### Switching to the bootloader 57 | 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. 58 | 59 | ### WebUSB 60 | 61 | The WebUSB isn't currently supported. 62 | 63 | ## Licensing 64 | 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. 65 | 66 | Non-libopencm3 related portions of the dapboot project are licensed under the less restrictive ISC license, except where otherwise specified in the headers of specific files. 67 | 68 | The UF2 parts are licensed under MIT. 69 | 70 | See the LICENSE file for full details. 71 | -------------------------------------------------------------------------------- /release.Makefile: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2017, Devan Lai 2 | ## 3 | ## Permission to use, copy, modify, and/or distribute this software 4 | ## for any purpose with or without fee is hereby granted, provided 5 | ## that the above copyright notice and this permission notice 6 | ## appear in all copies. 7 | ## 8 | ## THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 9 | ## WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 10 | ## WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 11 | ## AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 12 | ## CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 | ## LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 14 | ## NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 15 | ## CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | # This Makefile builds all official targets and places the firmware 18 | # bin files in the build/ directory. 19 | # 20 | # For normal development, use the Makefile in the src/ directory. 21 | 22 | # Be silent per default, but 'make V=1' will show all compiler calls. 23 | ifneq ($(V),1) 24 | Q := @ 25 | NULL := 2>/dev/null 26 | MAKE := $(MAKE) --no-print-directory 27 | endif 28 | export V 29 | 30 | BUILD_DIR ?= ./build 31 | 32 | all: dapboot-bluepill.bin \ 33 | dapboot-maplemini.bin \ 34 | dapboot-stlink.bin 35 | 36 | clean: 37 | $(Q)$(RM) $(BUILD_DIR)/*.bin 38 | $(Q)$(MAKE) -C src/ clean 39 | 40 | .PHONY = all clean 41 | 42 | $(BUILD_DIR): 43 | $(Q)mkdir -p $(BUILD_DIR) 44 | 45 | dapboot-bluepill.bin: | $(BUILD_DIR) 46 | @printf " BUILD $(@)\n" 47 | $(Q)$(MAKE) TARGET=BLUEPILL -C src/ clean 48 | $(Q)$(MAKE) TARGET=BLUEPILL -C src/ 49 | $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) 50 | 51 | dapboot-stlink.bin: | $(BUILD_DIR) 52 | @printf " BUILD $(@)\n" 53 | $(Q)$(MAKE) TARGET=STLINK -C src/ clean 54 | $(Q)$(MAKE) TARGET=STLINK -C src/ 55 | $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) 56 | 57 | dapboot-maplemini.bin: | $(BUILD_DIR) 58 | @printf " BUILD $(@)\n" 59 | $(Q)$(MAKE) TARGET=MAPLEMINI -C src/ clean 60 | $(Q)$(MAKE) TARGET=MAPLEMINI -C src/ 61 | $(Q)cp src/dapboot.bin $(BUILD_DIR)/$(@) 62 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2016, Devan Lai 2 | ## 3 | ## Permission to use, copy, modify, and/or distribute this software 4 | ## for any purpose with or without fee is hereby granted, provided 5 | ## that the above copyright notice and this permission notice 6 | ## appear in all copies. 7 | ## 8 | ## THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 9 | ## WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 10 | ## WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 11 | ## AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 12 | ## CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 | ## LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 14 | ## NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 15 | ## CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | -include local.mk 18 | 19 | BINARY = uf2boot 20 | OPENCM3_DIR = ../libopencm3 21 | 22 | UF2_VERSION_BASE = $(shell git describe --dirty --always --tags) 23 | VER_FLAGS = -DUF2_VERSION='"$(UF2_VERSION_BASE)"' 24 | 25 | TARGET ?= BLUEPILL 26 | include targets.mk 27 | 28 | SRCS := $(wildcard *.c) 29 | SRCS += $(wildcard $(TARGET_COMMON_DIR)/*.c) 30 | SRCS += $(wildcard $(TARGET_SPEC_DIR)/*.c) 31 | 32 | BUILD = build/$(TARGET) 33 | 34 | OBJS += $(addprefix $(BUILD)/,$(SRCS:.c=.o)) 35 | DEPS = $(addprefix $(BUILD)/,$(SRCS:.c=.d)) 36 | 37 | ELF = $(BUILD)/$(BINARY).elf 38 | 39 | all: $(BUILD) $(BUILD)/$(BINARY).bin size 40 | 41 | $(BUILD): 42 | mkdir -p $(BUILD)/stm32f103 43 | 44 | clean:: 45 | rm -rf build/ 46 | 47 | include rules.mk 48 | 49 | size: $(OBJS) $(ELF) 50 | @$(PREFIX)-size $(ELF) 51 | 52 | BMP = $(shell ls -1 /dev/cu.usbmodem* | head -1) 53 | BMP_ARGS = -ex "target extended-remote $(BMP)" -ex "mon swdp_scan" -ex "attach 1" 54 | GDB = arm-none-eabi-gdb 55 | 56 | flash: $(ELF) 57 | $(GDB) $(BMP_ARGS) -ex "load" -ex "quit" $(ELF) 58 | 59 | gdb: $(ELF) 60 | $(GDB) $(BMP_ARGS) $(ELF) 61 | 62 | ocd-gdb: $(ELF) 63 | -$(GDB) --eval "target remote | $(OOCD) -f $(OOCD_INTERFACE) -f $(OOCD_BOARD) -f debug.cfg" $(ELF) 64 | 65 | erase: 66 | $(OOCD) -f $(OOCD_INTERFACE) -f $(OOCD_BOARD) \ 67 | -c "init" -c "reset init" \ 68 | -c "stm32f1x unlock 0; reset halt" \ 69 | -c "flash erase_sector 0 0 last" -c "reset" -c "shutdown" 70 | 71 | .PHONY += debug size erase 72 | 73 | OBJS := $(sort $(OBJS)) 74 | 75 | # Add the base directory to the header search path 76 | CPPFLAGS += -I. 77 | 78 | # Add target config directory to the header search path 79 | CPPFLAGS += -I$(TARGET_COMMON_DIR)/ 80 | CPPFLAGS += -I$(TARGET_SPEC_DIR)/ 81 | 82 | DROPNAME = uf2-stm32f103-$(UF2_VERSION_BASE) 83 | 84 | do-drop: all 85 | mkdir -p build/$(DROPNAME) 86 | cp $(BUILD)/$(BINARY).bin build/$(DROPNAME)/$(BINARY)-$(TARGET).bin 87 | 88 | drop: 89 | rm -rf build 90 | set -e; for t in BLUEPILL JACDAC ; do $(MAKE) TARGET=$$t do-drop ; done 91 | cd build; 7z a $(DROPNAME).zip $(DROPNAME) 92 | -------------------------------------------------------------------------------- /src/blink/Makefile: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2016, Devan Lai 2 | ## 3 | ## Permission to use, copy, modify, and/or distribute this software 4 | ## for any purpose with or without fee is hereby granted, provided 5 | ## that the above copyright notice and this permission notice 6 | ## appear in all copies. 7 | ## 8 | ## THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 9 | ## WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 10 | ## WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 11 | ## AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 12 | ## CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 | ## LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 14 | ## NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 15 | ## CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | -include ../local.mk 18 | 19 | BINARY = blink 20 | OPENCM3_DIR = ../../libopencm3 21 | 22 | TARGET ?= BLUEPILL 23 | include ../targets.mk 24 | LDSCRIPT := ./stm32f103x8.ld 25 | 26 | SRCS := $(wildcard *.c) 27 | SRCS += $(wildcard ../$(TARGET_COMMON_DIR)/*.c) 28 | SRCS += $(wildcard ../$(TARGET_SPEC_DIR)/*.c) 29 | 30 | OBJS += $(SRCS:.c=.o) 31 | DEPS = $(SRCS:.c=.d) 32 | 33 | .DEFAULT_GOAL := $(BINARY).uf2 34 | 35 | $(BINARY).uf2: $(BINARY).bin 36 | python uf2conv.py -c -b 0x08004000 -o "$@" "$<" 37 | 38 | clean:: 39 | @rm -f $(OBJS) 40 | @rm -f $(DEPS) 41 | 42 | include ../rules.mk 43 | 44 | size: $(OBJS) $(BINARY).elf 45 | @$(PREFIX)-size $(OBJS) $(BINARY).elf 46 | 47 | debug: $(BINARY).elf 48 | -$(GDB) --eval "target remote | $(OOCD) -f $(OOCD_INTERFACE) -f $(OOCD_BOARD) -f debug.cfg" $(BINARY).elf 49 | 50 | .PHONY += debug size erase 51 | 52 | OBJS := $(sort $(OBJS)) 53 | 54 | # Add the base directory to the header search path 55 | CPPFLAGS += -I.. 56 | 57 | # Add target config directory to the header search path 58 | CPPFLAGS += -I../$(TARGET_COMMON_DIR)/ 59 | CPPFLAGS += -I../$(TARGET_SPEC_DIR)/ 60 | -------------------------------------------------------------------------------- /src/blink/blink.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Devan Lai 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software 5 | * for any purpose with or without fee is hereby granted, provided 6 | * that the above copyright notice and this permission notice 7 | * appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 13 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 15 | * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | #include 21 | #include "target.h" 22 | 23 | static void delay(int n) { 24 | for (int i = 0; i < n*1000000; ++i) 25 | asm("nop"); 26 | } 27 | 28 | int main(void) { 29 | /* Setup clocks */ 30 | target_clock_setup(); 31 | 32 | /* Initialize GPIO/LEDs if needed */ 33 | target_gpio_setup(); 34 | 35 | while (1) { 36 | target_set_led(1); 37 | delay(1); 38 | target_set_led(0); 39 | delay(1); 40 | } 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /src/blink/stm32f103x8.ld: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the libopencm3 project. 3 | * 4 | * Copyright (C) 2015 Karl Palsson 5 | * 6 | * This library is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this library. If not, see . 18 | */ 19 | 20 | /* Linker script for STM32F103x8, 64k flash, 20k RAM. */ 21 | 22 | /* Define memory regions. */ 23 | /* 8k for the bootloader */ 24 | MEMORY 25 | { 26 | rom (rx) : ORIGIN = 0x08004000, LENGTH = 100K 27 | ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K 28 | } 29 | 30 | /* Include the common ld script. */ 31 | INCLUDE libopencm3_stm32f1.ld 32 | -------------------------------------------------------------------------------- /src/blink/uf2conv.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # (c) Microsoft 3 | # License: MIT 4 | 5 | import sys 6 | import struct 7 | import subprocess 8 | import re 9 | import os 10 | import os.path 11 | import argparse 12 | 13 | UF2_MAGIC_START0 = 0x0A324655 # "UF2\n" 14 | UF2_MAGIC_START1 = 0x9E5D5157 # Randomly selected 15 | UF2_MAGIC_END = 0x0AB16F30 # Ditto 16 | 17 | INFO_FILE = "/INFO_UF2.TXT" 18 | 19 | appstartaddr = 0x2000 20 | 21 | def isUF2(buf): 22 | w = struct.unpack(" 476: 48 | assert False, "Invalid UF2 data size at " + ptr 49 | newaddr = hd[3] 50 | if curraddr == None: 51 | appstartaddr = newaddr 52 | curraddr = newaddr 53 | padding = newaddr - curraddr 54 | if padding < 0: 55 | assert False, "Block out of order at " + ptr 56 | if padding > 10*1024*1024: 57 | assert False, "More than 10M of padding needed at " + ptr 58 | if padding % 4 != 0: 59 | assert False, "Non-word padding size at " + ptr 60 | while padding > 0: 61 | padding -= 4 62 | outp += "\x00\x00\x00\x00" 63 | outp += block[32 : 32 + datalen] 64 | curraddr = newaddr + datalen 65 | return outp 66 | 67 | def convertToUF2(fileContent): 68 | datapadding = "" 69 | while len(datapadding) < 512 - 256 - 32 - 4: 70 | datapadding += "\x00\x00\x00\x00" 71 | numblocks = (len(fileContent) + 255) / 256 72 | outp = "" 73 | for blockno in range(0, numblocks): 74 | ptr = 256 * blockno 75 | chunk = fileContent[ptr:ptr + 256] 76 | hd = struct.pack("= 3 and words[1] == "2" and words[2] == "FAT": 152 | drives.append(words[0]) 153 | else: 154 | rootpath = "/media" 155 | if sys.platform == "darwin": 156 | rootpath = "/Volumes" 157 | elif sys.platform == "linux": 158 | tmp = rootpath + "/" + os.environ["USER"] 159 | if os.path.isdir(tmp): 160 | rootpath = tmp 161 | for d in os.listdir(rootpath): 162 | drives.append(os.path.join(rootpath, d)) 163 | 164 | def hasInfo(d): 165 | try: 166 | return os.path.isfile(d + INFO_FILE) 167 | except: 168 | return False 169 | 170 | return filter(hasInfo, drives) 171 | 172 | def boardID(path): 173 | with open(path + INFO_FILE, mode='r') as file: 174 | fileContent = file.read() 175 | return re.search("Board-ID: ([^\r\n]*)", fileContent).group(1) 176 | 177 | def listdrives(): 178 | for d in getdrives(): 179 | print d, boardID(d) 180 | 181 | def writeFile(name, buf): 182 | with open(name, "wb") as f: 183 | f.write(buf) 184 | print "Wrote %d bytes to %s." % (len(buf), name) 185 | 186 | def main(): 187 | global appstartaddr 188 | def error(msg): 189 | print msg 190 | sys.exit(1) 191 | parser = argparse.ArgumentParser(description='Convert to UF2 or flash directly.') 192 | parser.add_argument('input', metavar='INPUT', type=str, nargs='?', 193 | help='input file (HEX, BIN or UF2)') 194 | parser.add_argument('-b' , '--base', dest='base', type=str, 195 | default="0x2000", 196 | help='set base address of application for BIN format (default: 0x2000)') 197 | parser.add_argument('-o' , '--output', metavar="FILE", dest='output', type=str, 198 | help='write output to named file; defaults to "flash.uf2" or "flash.bin" where sensible') 199 | parser.add_argument('-d' , '--device', dest="device_path", 200 | help='select a device path to flash') 201 | parser.add_argument('-l' , '--list', action='store_true', 202 | help='list connected devices') 203 | parser.add_argument('-c' , '--convert', action='store_true', 204 | help='do not flash, just convert') 205 | args = parser.parse_args() 206 | appstartaddr = int(args.base, 0) 207 | if args.list: 208 | listdrives() 209 | else: 210 | if not args.input: 211 | error("Need input file") 212 | with open(args.input, mode='rb') as file: 213 | inpbuf = file.read() 214 | fromUF2 = isUF2(inpbuf) 215 | ext = "uf2" 216 | if fromUF2: 217 | outbuf = convertFromUF2(inpbuf) 218 | ext = "bin" 219 | elif isHEX(inpbuf): 220 | outbuf = convertFromHexToUF2(inpbuf) 221 | else: 222 | outbuf = convertToUF2(inpbuf) 223 | print "Converting to %s, output size: %d, start address: 0x%x" % (ext, len(outbuf), appstartaddr) 224 | 225 | if args.convert: 226 | drives = [] 227 | if args.output == None: 228 | args.output = "flash." + ext 229 | else: 230 | drives = getdrives() 231 | 232 | if args.output: 233 | writeFile(args.output, outbuf) 234 | else: 235 | if len(drives) == 0: 236 | error("No drive to deploy.") 237 | for d in drives: 238 | print "Flashing %s (%s)" % (d, boardID(d)) 239 | writeFile(outbuf, d + "/NEW.UF2") 240 | 241 | if __name__ == "__main__": 242 | main() 243 | -------------------------------------------------------------------------------- /src/bmp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | d=$(ls -1 /dev/cu.usbmodem* | head -1) 4 | cat < 20 | #include 21 | 22 | #include "uf2.h" 23 | #include "dapboot.h" 24 | #include "target.h" 25 | #include "usb_conf.h" 26 | #include "webusb.h" 27 | #include "winusb.h" 28 | #include "config.h" 29 | 30 | #include 31 | 32 | static inline void __set_MSP(uint32_t topOfMainStack) { 33 | asm("msr msp, %0" : : "r" (topOfMainStack)); 34 | } 35 | 36 | bool validate_application(void) { 37 | if ((*(volatile uint32_t *)APP_BASE_ADDRESS & 0x2FFE0000) == 0x20000000) { 38 | return true; 39 | } 40 | return false; 41 | } 42 | 43 | static void jump_to_application(void) __attribute__ ((noreturn)); 44 | 45 | static void jump_to_application(void) { 46 | vector_table_t* app_vector_table = (vector_table_t*)APP_BASE_ADDRESS; 47 | 48 | /* Use the application's vector table */ 49 | target_relocate_vector_table(); 50 | 51 | /* Do any necessary early setup for the application */ 52 | target_pre_main(); 53 | 54 | /* Initialize the application's stack pointer */ 55 | __set_MSP((uint32_t)(app_vector_table->initial_sp_value)); 56 | 57 | /* Jump to the application entry point */ 58 | app_vector_table->reset(); 59 | 60 | while (1); 61 | } 62 | 63 | uint32_t msTimer; 64 | extern int msc_started; 65 | 66 | int main(void) { 67 | bool appValid = validate_application(); 68 | 69 | if (appValid && target_get_force_app()) { 70 | jump_to_application(); 71 | return 0; 72 | } 73 | 74 | /* Setup clocks */ 75 | target_clock_setup(); 76 | 77 | /* Initialize GPIO/LEDs if needed */ 78 | target_gpio_setup(); 79 | 80 | if (target_get_force_bootloader() || !appValid) { 81 | /* Setup USB */ 82 | { 83 | char serial[USB_SERIAL_NUM_LENGTH+1]; 84 | serial[0] = '\0'; 85 | target_get_serial_number(serial, USB_SERIAL_NUM_LENGTH); 86 | usb_set_serial_number(serial); 87 | } 88 | 89 | usbd_device* usbd_dev = usb_setup(); 90 | //dfu_setup(usbd_dev, &target_manifest_app, NULL, NULL); 91 | usb_msc_init(usbd_dev, 0x82, 64, 0x01, 64, "Example Ltd", "UF2 Bootloader", 92 | "42.00", UF2_NUM_BLOCKS, read_block, write_block); 93 | winusb_setup(usbd_dev); 94 | 95 | int cycleCount = 0; 96 | int br = 500; 97 | int d = 1; 98 | 99 | while (1) { 100 | cycleCount++; 101 | 102 | target_set_led(cycleCount < br); 103 | 104 | if (cycleCount >= 700) { 105 | msTimer++; 106 | cycleCount = 0; 107 | 108 | br += d; 109 | if (br > 700) 110 | d = -2; 111 | else if (br < 10) 112 | d = 2; 113 | 114 | //int v = msTimer % 500; 115 | //target_set_led(v < 50); 116 | 117 | ghostfat_1ms(); 118 | 119 | if (appValid && !msc_started && msTimer > 3000) { 120 | target_manifest_app(); 121 | } 122 | } 123 | 124 | usbd_poll(usbd_dev); 125 | } 126 | } else { 127 | jump_to_application(); 128 | } 129 | 130 | return 0; 131 | } 132 | -------------------------------------------------------------------------------- /src/dapboot.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Devan Lai 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software 5 | * for any purpose with or without fee is hereby granted, provided 6 | * that the above copyright notice and this permission notice 7 | * appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 13 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 15 | * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef DAPBOOT_H_INCLUDED 20 | #define DAPBOOT_H_INCLUDED 21 | 22 | extern bool validate_application(void); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/debug.cfg: -------------------------------------------------------------------------------- 1 | gdb_port pipe 2 | gdb_memory_map disable 3 | 4 | $_TARGETNAME configure -event gdb-attach { 5 | echo "Halting target" 6 | halt 7 | } 8 | 9 | $_TARGETNAME configure -event gdb-detach { 10 | echo "Resetting target" 11 | reset 12 | } -------------------------------------------------------------------------------- /src/dmesg.c: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2017 Lancaster University. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a 7 | copy of this software and associated documentation files (the "Software"), 8 | to deal in the Software without restriction, including without limitation 9 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | and/or sell copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | DEALINGS IN THE SOFTWARE. 23 | */ 24 | 25 | #include "dmesg.h" 26 | #include 27 | #include "libopencm3/cm3/cortex.h" 28 | 29 | CodalLogStore codalLogStore; 30 | 31 | static void logwrite(const char *msg); 32 | 33 | static void logwriten(const char *msg, int l) 34 | { 35 | if (codalLogStore.ptr + l >= sizeof(codalLogStore.buffer)) 36 | { 37 | const int jump = sizeof(codalLogStore.buffer) / 4; 38 | codalLogStore.ptr -= jump; 39 | memmove(codalLogStore.buffer, codalLogStore.buffer + jump, codalLogStore.ptr); 40 | // zero-out the rest so it looks OK in the debugger 41 | memset(codalLogStore.buffer + codalLogStore.ptr, 0, sizeof(codalLogStore.buffer) - codalLogStore.ptr); 42 | } 43 | if (l + codalLogStore.ptr >= sizeof(codalLogStore.buffer)) 44 | { 45 | logwrite("DMESG line too long!\n"); 46 | return; 47 | } 48 | memcpy(codalLogStore.buffer + codalLogStore.ptr, msg, l); 49 | codalLogStore.ptr += l; 50 | codalLogStore.buffer[codalLogStore.ptr] = 0; 51 | } 52 | 53 | static void logwrite(const char *msg) 54 | { 55 | logwriten(msg, strlen(msg)); 56 | } 57 | 58 | static void writeDecNum(char *buf, int32_t n) 59 | { 60 | if (n < 0) { 61 | *buf++ = '-'; 62 | n = -n; 63 | } 64 | 65 | if (n == 0) { 66 | *buf++ = '0'; 67 | *buf++ = 0; 68 | return; 69 | } 70 | 71 | char tmp[20]; 72 | int i = 0; 73 | while (n > 0) { 74 | tmp[i++] = (n % 10) + '0'; 75 | n /= 10; 76 | } 77 | 78 | while (--i > 0) { 79 | *buf++ = tmp[i]; 80 | } 81 | 82 | *buf = 0; 83 | } 84 | 85 | static void writeNum(char *buf, uint32_t n, bool full) 86 | { 87 | int i = 0; 88 | int sh = 28; 89 | while (sh >= 0) 90 | { 91 | int d = (n >> sh) & 0xf; 92 | if (full || d || sh == 0 || i) 93 | { 94 | buf[i++] = d > 9 ? 'A' + d - 10 : '0' + d; 95 | } 96 | sh -= 4; 97 | } 98 | buf[i] = 0; 99 | } 100 | 101 | static void logwritenum(uint32_t n, bool full, bool hex) 102 | { 103 | char buff[20]; 104 | 105 | if (hex) 106 | { 107 | writeNum(buff, n, full); 108 | logwrite("0x"); 109 | } 110 | else 111 | { 112 | writeDecNum(buff, n); 113 | } 114 | 115 | logwrite(buff); 116 | } 117 | 118 | void codal_dmesg(const char *format, ...) 119 | { 120 | va_list arg; 121 | va_start(arg, format); 122 | codal_vdmesg(format, arg); 123 | va_end(arg); 124 | } 125 | 126 | void codal_vdmesg(const char *format, va_list ap) 127 | { 128 | const char *end = format; 129 | 130 | CM_ATOMIC_BLOCK() { 131 | 132 | while (*end) 133 | { 134 | if (*end++ == '%') 135 | { 136 | logwriten(format, end - format - 1); 137 | uint32_t val = va_arg(ap, uint32_t); 138 | switch (*end++) 139 | { 140 | case 'c': 141 | logwriten((const char *)&val, 1); 142 | break; 143 | case 'd': 144 | logwritenum(val, false, false); 145 | break; 146 | case 'x': 147 | logwritenum(val, false, true); 148 | break; 149 | case 'p': 150 | case 'X': 151 | logwritenum(val, true, true); 152 | break; 153 | case 's': 154 | logwrite((char *)(void *)val); 155 | break; 156 | case '%': 157 | logwrite("%"); 158 | break; 159 | default: 160 | logwrite("???"); 161 | break; 162 | } 163 | format = end; 164 | } 165 | } 166 | logwriten(format, end - format); 167 | logwrite("\n"); 168 | 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/dmesg.h: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2017 Lancaster University. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a 7 | copy of this software and associated documentation files (the "Software"), 8 | to deal in the Software without restriction, including without limitation 9 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | and/or sell copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | DEALINGS IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef CODAL_DMESG_H 26 | #define CODAL_DMESG_H 27 | 28 | #define DEVICE_DMESG_BUFFER_SIZE 1024 29 | 30 | #if DEVICE_DMESG_BUFFER_SIZE > 0 31 | 32 | #include 33 | #include 34 | 35 | #if DEVICE_DMESG_BUFFER_SIZE < 256 36 | #error "Too small DMESG buffer" 37 | #endif 38 | 39 | typedef struct CodalLogStore 40 | { 41 | uint32_t ptr; 42 | char buffer[DEVICE_DMESG_BUFFER_SIZE]; 43 | } CodalLogStore; 44 | extern CodalLogStore codalLogStore; 45 | 46 | /** 47 | * Log formatted message to an internal buffer. 48 | * 49 | * Supported format strings: 50 | * %c - single character 51 | * %d - decimal number 52 | * %x - hexadecimal number (with 0x) 53 | * %p - hexadecimal number padded with zeros (and with 0x) 54 | * %X - hexadecimal number padded with zeros (and with 0x) 55 | * %s - '\0'-terminated string 56 | * %% - literal % 57 | * Typically used via the DMESG() macro. 58 | * 59 | * @param format Format string 60 | * 61 | * @code 62 | * uint32_t k; 63 | * void *ptr; 64 | * ... 65 | * DMESG("USB: Error #%d at %X", k, ptr); 66 | * @endcode 67 | */ 68 | void codal_dmesg(const char *format, ...); 69 | void codal_vdmesg(const char *format, va_list ap); 70 | 71 | #define DMESG codal_dmesg 72 | 73 | #else 74 | 75 | #define DMESG(...) ((void)0) 76 | 77 | #endif 78 | 79 | #define NOOP(...) do{}while(0) 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /src/dummy.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Devan Lai 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software 5 | * for any purpose with or without fee is hereby granted, provided 6 | * that the above copyright notice and this permission notice 7 | * appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 13 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 15 | * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* Default dummy implementations for optional target functions */ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | void target_get_serial_number(char* dest, size_t max_chars) __attribute__((weak)); 26 | void target_log(const char* str) __attribute__((weak)); 27 | void target_manifest_app(void) __attribute__((weak)); 28 | void target_pre_main(void) __attribute__((weak)); 29 | 30 | void target_get_serial_number(char* dest, size_t max_chars) { 31 | (void)max_chars; 32 | if (dest) { 33 | dest[0] = '\0'; 34 | } 35 | } 36 | 37 | void target_log(const char* str) { 38 | (void)str; 39 | } 40 | 41 | void target_manifest_app(void) { 42 | scb_reset_system(); 43 | } 44 | 45 | void target_pre_main(void) 46 | { 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/gdb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ./bmp.sh > build/debug.gdb 4 | arm-none-eabi-gdb --command=build/debug.gdb 5 | -------------------------------------------------------------------------------- /src/ghostfat.c: -------------------------------------------------------------------------------- 1 | 2 | #include "uf2.h" 3 | 4 | #include 5 | #include "target.h" 6 | #include "dmesg.h" 7 | 8 | typedef struct { 9 | uint8_t JumpInstruction[3]; 10 | uint8_t OEMInfo[8]; 11 | uint16_t SectorSize; 12 | uint8_t SectorsPerCluster; 13 | uint16_t ReservedSectors; 14 | uint8_t FATCopies; 15 | uint16_t RootDirectoryEntries; 16 | uint16_t TotalSectors16; 17 | uint8_t MediaDescriptor; 18 | uint16_t SectorsPerFAT; 19 | uint16_t SectorsPerTrack; 20 | uint16_t Heads; 21 | uint32_t HiddenSectors; 22 | uint32_t TotalSectors32; 23 | uint8_t PhysicalDriveNum; 24 | uint8_t Reserved; 25 | uint8_t ExtendedBootSig; 26 | uint32_t VolumeSerialNumber; 27 | uint8_t VolumeLabel[11]; 28 | uint8_t FilesystemIdentifier[8]; 29 | } __attribute__((packed)) FAT_BootBlock; 30 | 31 | typedef struct { 32 | char name[8]; 33 | char ext[3]; 34 | uint8_t attrs; 35 | uint8_t reserved; 36 | uint8_t createTimeFine; 37 | uint16_t createTime; 38 | uint16_t createDate; 39 | uint16_t lastAccessDate; 40 | uint16_t highStartCluster; 41 | uint16_t updateTime; 42 | uint16_t updateDate; 43 | uint16_t startCluster; 44 | uint32_t size; 45 | } __attribute__((packed)) DirEntry; 46 | 47 | static size_t flashSize(void) { 48 | return FLASH_SIZE_OVERRIDE; 49 | } 50 | 51 | //#define DBG NOOP 52 | #define DBG DMESG 53 | 54 | struct TextFile { 55 | const char name[11]; 56 | const char *content; 57 | }; 58 | 59 | #define NUM_FAT_BLOCKS UF2_NUM_BLOCKS 60 | 61 | #define STR0(x) #x 62 | #define STR(x) STR0(x) 63 | const char infoUf2File[] = // 64 | "UF2 Bootloader " UF2_VERSION "\r\n" 65 | "Model: " PRODUCT_NAME "\r\n" 66 | "Board-ID: " BOARD_ID "\r\n"; 67 | 68 | const char indexFile[] = // 69 | "\n" 70 | "" 71 | "" 72 | "" 75 | "" 76 | "\n"; 77 | 78 | static const struct TextFile info[] = { 79 | {.name = "INFO_UF2TXT", .content = infoUf2File}, 80 | {.name = "INDEX HTM", .content = indexFile}, 81 | {.name = "CURRENT UF2"}, 82 | }; 83 | #define NUM_INFO (int)(sizeof(info) / sizeof(info[0])) 84 | 85 | #define UF2_SIZE (flashSize() * 2) 86 | #define UF2_SECTORS (UF2_SIZE / 512) 87 | #define UF2_FIRST_SECTOR (NUM_INFO + 1) 88 | #define UF2_LAST_SECTOR (uint32_t)(UF2_FIRST_SECTOR + UF2_SECTORS - 1) 89 | 90 | #define RESERVED_SECTORS 1 91 | #define ROOT_DIR_SECTORS 4 92 | #define SECTORS_PER_FAT ((NUM_FAT_BLOCKS * 2 + 511) / 512) 93 | 94 | #define START_FAT0 RESERVED_SECTORS 95 | #define START_FAT1 (START_FAT0 + SECTORS_PER_FAT) 96 | #define START_ROOTDIR (START_FAT1 + SECTORS_PER_FAT) 97 | #define START_CLUSTERS (START_ROOTDIR + ROOT_DIR_SECTORS) 98 | 99 | static const FAT_BootBlock BootBlock = { 100 | .JumpInstruction = {0xeb, 0x3c, 0x90}, 101 | .OEMInfo = "UF2 UF2 ", 102 | .SectorSize = 512, 103 | .SectorsPerCluster = 1, 104 | .ReservedSectors = RESERVED_SECTORS, 105 | .FATCopies = 2, 106 | .RootDirectoryEntries = (ROOT_DIR_SECTORS * 512 / 32), 107 | .TotalSectors16 = NUM_FAT_BLOCKS - 2, 108 | .MediaDescriptor = 0xF8, 109 | .SectorsPerFAT = SECTORS_PER_FAT, 110 | .SectorsPerTrack = 1, 111 | .Heads = 1, 112 | .ExtendedBootSig = 0x29, 113 | .VolumeSerialNumber = 0x00420042, 114 | .VolumeLabel = VOLUME_LABEL, 115 | .FilesystemIdentifier = "FAT16 ", 116 | }; 117 | 118 | #define NO_CACHE 0xffffffff 119 | 120 | static uint32_t flashAddr = NO_CACHE; 121 | static uint8_t flashBuf[FLASH_PAGE_SIZE] __attribute__((aligned(4))); 122 | static bool firstFlush = true; 123 | static bool hadWrite = false; 124 | static uint32_t ms; 125 | static uint32_t resetTime; 126 | static uint32_t lastFlush; 127 | 128 | static void flushFlash(void) { 129 | lastFlush = ms; 130 | if (flashAddr == NO_CACHE) 131 | return; 132 | 133 | if (firstFlush) { 134 | firstFlush = false; 135 | 136 | // disable bootloader or something 137 | } 138 | 139 | DBG("Flush at %x", flashAddr); 140 | if (memcmp(flashBuf, (void *)flashAddr, FLASH_PAGE_SIZE) != 0) { 141 | DBG("Write flush at %x", flashAddr); 142 | 143 | target_flash_unlock(); 144 | bool ok = target_flash_program_array((void *)flashAddr, (void*)flashBuf, FLASH_PAGE_SIZE / 2); 145 | target_flash_lock(); 146 | (void)ok; 147 | } 148 | 149 | flashAddr = NO_CACHE; 150 | } 151 | 152 | static void flash_write(uint32_t dst, const uint8_t *src, int len) { 153 | uint32_t newAddr = dst & ~(FLASH_PAGE_SIZE - 1); 154 | 155 | hadWrite = true; 156 | 157 | if (newAddr != flashAddr) { 158 | flushFlash(); 159 | flashAddr = newAddr; 160 | memcpy(flashBuf, (void *)newAddr, FLASH_PAGE_SIZE); 161 | } 162 | memcpy(flashBuf + (dst & (FLASH_PAGE_SIZE - 1)), src, len); 163 | } 164 | 165 | static void uf2_timer_start(int delay) { 166 | resetTime = ms + delay; 167 | } 168 | 169 | // called roughly every 1ms 170 | void ghostfat_1ms() { 171 | ms++; 172 | 173 | if (resetTime && ms >= resetTime) { 174 | flushFlash(); 175 | target_manifest_app(); 176 | while (1); 177 | } 178 | 179 | if (lastFlush && ms - lastFlush > 100) { 180 | flushFlash(); 181 | } 182 | } 183 | 184 | static void padded_memcpy(char *dst, const char *src, int len) { 185 | for (int i = 0; i < len; ++i) { 186 | if (*src) 187 | *dst = *src++; 188 | else 189 | *dst = ' '; 190 | dst++; 191 | } 192 | } 193 | 194 | int read_block(uint32_t block_no, uint8_t *data) { 195 | memset(data, 0, 512); 196 | uint32_t sectionIdx = block_no; 197 | 198 | if (block_no == 0) { 199 | memcpy(data, &BootBlock, sizeof(BootBlock)); 200 | data[510] = 0x55; 201 | data[511] = 0xaa; 202 | // logval("data[0]", data[0]); 203 | } else if (block_no < START_ROOTDIR) { 204 | sectionIdx -= START_FAT0; 205 | // logval("sidx", sectionIdx); 206 | if (sectionIdx >= SECTORS_PER_FAT) 207 | sectionIdx -= SECTORS_PER_FAT; 208 | if (sectionIdx == 0) { 209 | data[0] = 0xf0; 210 | for (int i = 1; i < NUM_INFO * 2 + 4; ++i) { 211 | data[i] = 0xff; 212 | } 213 | } 214 | for (int i = 0; i < 256; ++i) { 215 | uint32_t v = sectionIdx * 256 + i; 216 | if (UF2_FIRST_SECTOR <= v && v <= UF2_LAST_SECTOR) 217 | ((uint16_t *)(void *)data)[i] = v == UF2_LAST_SECTOR ? 0xffff : v + 1; 218 | } 219 | } else if (block_no < START_CLUSTERS) { 220 | sectionIdx -= START_ROOTDIR; 221 | if (sectionIdx == 0) { 222 | DirEntry *d = (void *)data; 223 | padded_memcpy(d->name, (const char *)BootBlock.VolumeLabel, 11); 224 | d->attrs = 0x28; 225 | for (int i = 0; i < NUM_INFO; ++i) { 226 | d++; 227 | const struct TextFile *inf = &info[i]; 228 | d->size = inf->content ? strlen(inf->content) : UF2_SIZE; 229 | d->startCluster = i + 2; 230 | padded_memcpy(d->name, inf->name, 11); 231 | } 232 | } 233 | } else { 234 | sectionIdx -= START_CLUSTERS; 235 | if (sectionIdx < NUM_INFO - 1) { 236 | memcpy(data, info[sectionIdx].content, strlen(info[sectionIdx].content)); 237 | } else { 238 | sectionIdx -= NUM_INFO - 1; 239 | uint32_t addr = sectionIdx * 256; 240 | if (addr < flashSize()) { 241 | UF2_Block *bl = (void *)data; 242 | bl->magicStart0 = UF2_MAGIC_START0; 243 | bl->magicStart1 = UF2_MAGIC_START1; 244 | bl->magicEnd = UF2_MAGIC_END; 245 | bl->blockNo = sectionIdx; 246 | bl->numBlocks = flashSize() / 256; 247 | bl->targetAddr = addr | 0x8000000; 248 | bl->payloadSize = 256; 249 | memcpy(bl->data, (void *)addr, bl->payloadSize); 250 | } 251 | } 252 | } 253 | 254 | return 0; 255 | } 256 | 257 | static void write_block_core(uint32_t block_no, const uint8_t *data, bool quiet, WriteState *state) { 258 | const UF2_Block *bl = (const void *)data; 259 | 260 | (void)block_no; 261 | 262 | // DBG("Write magic: %x", bl->magicStart0); 263 | 264 | if (!is_uf2_block(bl) || !UF2_IS_MY_FAMILY(bl)) { 265 | return; 266 | } 267 | 268 | if ((bl->flags & UF2_FLAG_NOFLASH) || bl->payloadSize > 256 || (bl->targetAddr & 0xff) || 269 | bl->targetAddr < USER_FLASH_START || bl->targetAddr + bl->payloadSize > USER_FLASH_END) { 270 | DBG("Skip block at %x", bl->targetAddr); 271 | // this happens when we're trying to re-flash CURRENT.UF2 file previously 272 | // copied from a device; we still want to count these blocks to reset properly 273 | } else { 274 | // logval("write block at", bl->targetAddr); 275 | DBG("Write block at %x", bl->targetAddr); 276 | flash_write(bl->targetAddr, bl->data, bl->payloadSize); 277 | } 278 | 279 | bool isSet = false; 280 | 281 | if (state && bl->numBlocks) { 282 | if (state->numBlocks != bl->numBlocks) { 283 | if (bl->numBlocks >= MAX_BLOCKS || state->numBlocks) 284 | state->numBlocks = 0xffffffff; 285 | else 286 | state->numBlocks = bl->numBlocks; 287 | } 288 | if (bl->blockNo < MAX_BLOCKS) { 289 | uint8_t mask = 1 << (bl->blockNo % 8); 290 | uint32_t pos = bl->blockNo / 8; 291 | if (!(state->writtenMask[pos] & mask)) { 292 | // logval("incr", state->numWritten); 293 | state->writtenMask[pos] |= mask; 294 | state->numWritten++; 295 | } 296 | if (state->numWritten >= state->numBlocks) { 297 | // wait a little bit before resetting, to avoid Windows transmit error 298 | // https://github.com/Microsoft/uf2-samd21/issues/11 299 | if (!quiet) { 300 | uf2_timer_start(30); 301 | isSet = true; 302 | } 303 | } 304 | } 305 | //DBG("wr %d=%d (of %d)", state->numWritten, bl->blockNo, bl->numBlocks); 306 | } 307 | 308 | if (!isSet && !quiet) { 309 | uf2_timer_start(500); 310 | } 311 | } 312 | 313 | 314 | WriteState wrState; 315 | 316 | int write_block(uint32_t lba, const uint8_t *copy_from) 317 | { 318 | target_set_led((wrState.numWritten * 17) & 1); 319 | write_block_core(lba, copy_from, false, &wrState); 320 | target_set_led(0); 321 | return 0; 322 | } 323 | -------------------------------------------------------------------------------- /src/rules.mk: -------------------------------------------------------------------------------- 1 | ## 2 | ## This file is derived from the libopencm3 project. 3 | ## 4 | ## Copyright (C) 2009 Uwe Hermann 5 | ## Copyright (C) 2010 Piotr Esden-Tempski 6 | ## Copyright (C) 2013 Frantisek Burian 7 | ## Copyright (C) 2016 Devan Lai 8 | ## 9 | ## This library is free software: you can redistribute it and/or modify 10 | ## it under the terms of the GNU Lesser General Public License as published by 11 | ## the Free Software Foundation, either version 3 of the License, or 12 | ## (at your option) any later version. 13 | ## 14 | ## This library is distributed in the hope that it will be useful, 15 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ## GNU Lesser General Public License for more details. 18 | ## 19 | ## You should have received a copy of the GNU Lesser General Public License 20 | ## along with this library. If not, see . 21 | ## 22 | 23 | # Be silent per default, but 'make V=1' will show all compiler calls. 24 | ifneq ($(V),1) 25 | Q := @ 26 | NULL := 2>/dev/null 27 | endif 28 | 29 | #################################################################### 30 | # Target Architecture flags 31 | ifeq ($(ARCH),STM32F0) 32 | LIBNAME = opencm3_stm32f0 33 | DEFS += -DSTM32F0 34 | FP_FLAGS ?= -msoft-float 35 | ARCH_FLAGS = -mthumb -mcpu=cortex-m0 $(FP_FLAGS) 36 | OOCD_BOARD ?= target/stm32f0x.cfg 37 | endif 38 | ifeq ($(ARCH),STM32F1) 39 | LIBNAME = opencm3_stm32f1 40 | DEFS += -DSTM32F1 41 | FP_FLAGS ?= -msoft-float 42 | ARCH_FLAGS = -mthumb -mcpu=cortex-m3 $(FP_FLAGS) -mfix-cortex-m3-ldrd 43 | OOCD_BOARD ?= target/stm32f1x.cfg 44 | endif 45 | 46 | LIBNAME ?= opencm3_stm32f0 47 | DEFS ?= -DSTM32F0 48 | FP_FLAGS ?= -msoft-float 49 | ARCH_FLAGS ?= -mthumb -mcpu=cortex-m0 $(FP_FLAGS) 50 | 51 | #################################################################### 52 | # Semihosting support 53 | SEMIHOSTING ?= 0 54 | 55 | ifeq ($(SEMIHOSTING),1) 56 | LDFLAGS += --specs=rdimon.specs 57 | LDLIBS += -lrdimon 58 | DEFS += -DSEMIHOSTING=1 59 | else 60 | DEFS += -DSEMIHOSTING=0 61 | endif 62 | 63 | #################################################################### 64 | # OpenOCD specific variables 65 | 66 | OOCD ?= openocd 67 | OOCD_INTERFACE ?= interface/stlink-v2.cfg 68 | OOCD_BOARD ?= target/stm32f1x.cfg 69 | 70 | #################################################################### 71 | # Executables 72 | 73 | PREFIX ?= arm-none-eabi 74 | 75 | CC := $(PREFIX)-gcc 76 | CXX := $(PREFIX)-g++ 77 | LD := $(PREFIX)-gcc 78 | AR := $(PREFIX)-ar 79 | AS := $(PREFIX)-as 80 | OBJCOPY := $(PREFIX)-objcopy 81 | OBJDUMP := $(PREFIX)-objdump 82 | GDB := $(PREFIX)-gdb 83 | STFLASH = $(shell which st-flash) 84 | 85 | #################################################################### 86 | # Source files 87 | 88 | INCLUDE_DIR = $(OPENCM3_DIR)/include 89 | LIB_DIR = $(OPENCM3_DIR)/lib 90 | 91 | #################################################################### 92 | # C flags 93 | 94 | CFLAGS += -Os -g -std=gnu11 95 | CFLAGS += -Wextra -Wshadow -Wimplicit-function-declaration 96 | CFLAGS += -Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes 97 | CFLAGS += -fno-common -ffunction-sections -fdata-sections 98 | 99 | #################################################################### 100 | # C++ flags 101 | 102 | CXXFLAGS += -Os -g 103 | CXXFLAGS += -Wextra -Wshadow -Wredundant-decls -Weffc++ 104 | CXXFLAGS += -fno-common -ffunction-sections -fdata-sections 105 | 106 | #################################################################### 107 | # C & C++ preprocessor common flags 108 | 109 | CPPFLAGS += -MD 110 | CPPFLAGS += -Wall -Wundef 111 | CPPFLAGS += -I$(INCLUDE_DIR) $(DEFS) 112 | 113 | #################################################################### 114 | # Linker flags 115 | 116 | LDFLAGS += --static -nostartfiles 117 | LDFLAGS += -L$(LIB_DIR) 118 | LDFLAGS += -T$(LDSCRIPT) 119 | LDFLAGS += -Wl,-Map=$(*).map 120 | LDFLAGS += -Wl,--gc-sections 121 | ifeq ($(V),99) 122 | LDFLAGS += -Wl,--print-gc-sections 123 | endif 124 | 125 | #################################################################### 126 | # Used libraries 127 | 128 | LDLIBS += -l$(LIBNAME) 129 | LDLIBS += -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group 130 | 131 | #################################################################### 132 | #################################################################### 133 | #################################################################### 134 | 135 | .SUFFIXES: .elf .bin .hex .srec .list .map .images 136 | .SECONDEXPANSION: 137 | .SECONDARY: 138 | 139 | elf: $(BINARY).elf 140 | bin: $(BINARY).bin 141 | hex: $(BINARY).hex 142 | srec: $(BINARY).srec 143 | list: $(BINARY).list 144 | 145 | images: $(BUILD)/$(BINARY).images 146 | ocd-flash: $(BUILD)/$(BINARY).flash 147 | 148 | $(LDSCRIPT): 149 | ifeq (,$(wildcard $(LDSCRIPT))) 150 | $(error Unable to find specified linker script: $(LDSCRIPT)) 151 | endif 152 | 153 | $(OPENCM3_DIR)/Makefile: 154 | $(Q)git submodule update --init $(OPENCM3_DIR) 155 | 156 | $(LIB_DIR)/lib$(LIBNAME).a: $(OPENCM3_DIR)/Makefile 157 | $(Q)$(MAKE) -C $(OPENCM3_DIR) 158 | 159 | locm3: $(LIB_DIR)/lib$(LIBNAME).a 160 | 161 | %.images: %.bin %.hex %.srec %.list %.map 162 | @#printf "*** $* images generated ***\n" 163 | 164 | %.bin: %.elf 165 | @#printf " OBJCOPY $(*).bin\n" 166 | $(Q)$(OBJCOPY) -Obinary $(*).elf $(*).tmpbin 167 | $(Q)(cat $(*).tmpbin; cat /dev/zero) | head -c 16384 > $(*).bin 168 | 169 | %.hex: %.elf 170 | @#printf " OBJCOPY $(*).hex\n" 171 | $(Q)$(OBJCOPY) -Oihex $(*).elf $(*).hex 172 | 173 | %.srec: %.elf 174 | @#printf " OBJCOPY $(*).srec\n" 175 | $(Q)$(OBJCOPY) -Osrec $(*).elf $(*).srec 176 | 177 | %.list: %.elf 178 | @#printf " OBJDUMP $(*).list\n" 179 | $(Q)$(OBJDUMP) -S $(*).elf > $(*).list 180 | 181 | %.elf %.map: $(OBJS) $(LDSCRIPT) $(LIB_DIR)/lib$(LIBNAME).a 182 | @#printf " LD $(*).elf\n" 183 | $(Q)$(LD) $(LDFLAGS) $(ARCH_FLAGS) $(OBJS) $(LDLIBS) -o $(*).elf 184 | 185 | $(BUILD)/%.o: %.c $(LIB_DIR)/lib$(LIBNAME).a 186 | @printf " CC $(*).c\n" 187 | $(Q)$(CC) $(CFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) $(VER_FLAGS) -o $@ -c $(*).c 188 | 189 | clean:: 190 | @#printf " CLEAN\n" 191 | $(Q)$(RM) *.o *.d *.elf *.bin *.hex *.srec *.list *.map 192 | 193 | %.stlink-flash: %.bin 194 | @printf " FLASH $<\n" 195 | $(Q)$(STFLASH) write $(*).bin 0x08000000 196 | 197 | %.flash: %.elf 198 | @printf " FLASH $<\n" 199 | $(Q)$(OOCD) -f $(OOCD_INTERFACE) \ 200 | -f $(OOCD_BOARD) \ 201 | -c "program $(*).elf verify reset exit" \ 202 | $(NULL) 203 | 204 | .PHONY: images clean elf bin hex srec list locm3 205 | 206 | -include $(OBJS:.o=.d) 207 | -------------------------------------------------------------------------------- /src/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | make 5 | (./bmp.sh && echo load && echo quit) > build/flash.gdb 6 | arm-none-eabi-gdb --command=build/flash.gdb 7 | -------------------------------------------------------------------------------- /src/stm32f103/backup.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Devan Lai 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software 5 | * for any purpose with or without fee is hereby granted, provided 6 | * that the above copyright notice and this permission notice 7 | * appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 13 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 15 | * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "backup.h" 24 | 25 | #define RTC_BKP_DR(reg) MMIO16(BACKUP_REGS_BASE + 4 + (4 * (reg))) 26 | 27 | void backup_write(enum BackupRegister reg, uint32_t value) { 28 | /* 29 | rcc_periph_clock_enable(RCC_PWR); 30 | rcc_periph_clock_enable(RCC_BKP); 31 | 32 | pwr_disable_backup_domain_write_protect(); 33 | RTC_BKP_DR((int)reg*2) = value & 0xFFFFUL; 34 | RTC_BKP_DR((int)reg*2+1) = (value & 0xFFFF0000UL) >> 16; 35 | pwr_enable_backup_domain_write_protect(); 36 | */ 37 | (void)reg; 38 | *(volatile uint32_t*)0x20004000 = value; 39 | } 40 | 41 | uint32_t backup_read(enum BackupRegister reg) { 42 | (void)reg; 43 | return *(volatile uint32_t*)0x20004000; 44 | /* 45 | uint32_t value = ((uint32_t)RTC_BKP_DR((int)reg*2+1) << 16) 46 | | ((uint32_t)RTC_BKP_DR((int)reg*2) << 0); 47 | return value; 48 | */ 49 | } 50 | -------------------------------------------------------------------------------- /src/stm32f103/backup.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Devan Lai 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software 5 | * for any purpose with or without fee is hereby granted, provided 6 | * that the above copyright notice and this permission notice 7 | * appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 13 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 15 | * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef BACKUP_H_INCLUDED 20 | #define BACKUP_H_INCLUDED 21 | 22 | enum BackupRegister { 23 | BKP0 = 0, 24 | BKP1, 25 | BKP2, 26 | BKP3, 27 | BKP4, 28 | }; 29 | 30 | extern void backup_write(enum BackupRegister reg, uint32_t value); 31 | extern uint32_t backup_read(enum BackupRegister reg); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/stm32f103/bluepill/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Devan Lai 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software 5 | * for any purpose with or without fee is hereby granted, provided 6 | * that the above copyright notice and this permission notice 7 | * appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 13 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 15 | * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef CONFIG_H_INCLUDED 20 | #define CONFIG_H_INCLUDED 21 | 22 | #define APP_BASE_ADDRESS 0x08004000 23 | #define FLASH_SIZE_OVERRIDE 0x20000 24 | #define FLASH_PAGE_SIZE 1024 25 | #define DFU_UPLOAD_AVAILABLE 1 26 | #define DFU_DOWNLOAD_AVAILABLE 1 27 | 28 | #ifndef HAVE_LED 29 | #define HAVE_LED 0 30 | #endif 31 | 32 | #ifndef HAVE_BUTTON 33 | #define HAVE_BUTTON 0 34 | #endif 35 | 36 | #ifndef HAVE_USB_PULLUP_CONTROL 37 | #define HAVE_USB_PULLUP_CONTROL 0 38 | #endif 39 | 40 | #define UF2_FAMILY 0x5ee21072 41 | 42 | #undef VOLUME_LABEL 43 | #define VOLUME_LABEL "BLUEPILL" 44 | #undef PRODUCT_NAME 45 | #define PRODUCT_NAME "Blue Pill STM32F103xB" 46 | #undef BOARD_ID 47 | #define BOARD_ID "STM32F103-blue-pill-v0" 48 | 49 | //#define DOUBLE_TAP 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/stm32f103/generic/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Devan Lai 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software 5 | * for any purpose with or without fee is hereby granted, provided 6 | * that the above copyright notice and this permission notice 7 | * appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 13 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 15 | * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef CONFIG_H_INCLUDED 20 | #define CONFIG_H_INCLUDED 21 | 22 | #define APP_BASE_ADDRESS 0x08004000 23 | #define FLASH_SIZE_OVERRIDE 0x20000 24 | #define FLASH_PAGE_SIZE 1024 25 | #define DFU_UPLOAD_AVAILABLE 1 26 | #define DFU_DOWNLOAD_AVAILABLE 1 27 | 28 | #ifndef HAVE_LED 29 | #define HAVE_LED 0 30 | #endif 31 | 32 | #ifndef HAVE_BUTTON 33 | #define HAVE_BUTTON 0 34 | #endif 35 | 36 | #ifndef HAVE_USB_PULLUP_CONTROL 37 | #define HAVE_USB_PULLUP_CONTROL 0 38 | #endif 39 | 40 | #define UF2_FAMILY 0x5ee21072 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/stm32f103/jacdac/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Devan Lai 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software 5 | * for any purpose with or without fee is hereby granted, provided 6 | * that the above copyright notice and this permission notice 7 | * appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 13 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 15 | * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef CONFIG_H_INCLUDED 20 | #define CONFIG_H_INCLUDED 21 | 22 | #define APP_BASE_ADDRESS 0x08004000 23 | #define FLASH_SIZE_OVERRIDE 0x20000 24 | #define FLASH_PAGE_SIZE 1024 25 | #define DFU_UPLOAD_AVAILABLE 1 26 | #define DFU_DOWNLOAD_AVAILABLE 1 27 | 28 | #define HAVE_LED 1 29 | #define HAVE_BUTTON 0 30 | 31 | #ifndef HAVE_USB_PULLUP_CONTROL 32 | #define HAVE_USB_PULLUP_CONTROL 0 33 | #endif 34 | 35 | #define UF2_FAMILY 0x5ee21072 36 | 37 | #undef VOLUME_LABEL 38 | #define VOLUME_LABEL "JACDAC" 39 | #undef PRODUCT_NAME 40 | #define PRODUCT_NAME "JACDAC Feather STM32F103CB" 41 | #undef BOARD_ID 42 | #define BOARD_ID "STM32F103-jacdac-feather-v0" 43 | 44 | #define CRYSTAL_16MHZ 1 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /src/stm32f103/maplemini/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Devan Lai 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software 5 | * for any purpose with or without fee is hereby granted, provided 6 | * that the above copyright notice and this permission notice 7 | * appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 13 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 15 | * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef CONFIG_H_INCLUDED 20 | #define CONFIG_H_INCLUDED 21 | 22 | #define APP_BASE_ADDRESS 0x08004000 23 | #define FLASH_PAGE_SIZE 1024 24 | #define DFU_UPLOAD_AVAILABLE 1 25 | #define DFU_DOWNLOAD_AVAILABLE 1 26 | 27 | #define HAVE_LED 1 28 | #define LED_GPIO_PORT GPIOB 29 | #define LED_GPIO_PIN GPIO1 30 | #define LED_OPEN_DRAIN 0 31 | 32 | /* Technically, there is a button on PB8, but the button is 33 | also shorted to BOOT0, so it's not very useful for us to 34 | sample PB8 on boot, since pulling it high will already 35 | trigger the ROM serial bootloader and prevent us from 36 | running anyways. */ 37 | #define HAVE_BUTTON 0 38 | 39 | #define HAVE_USB_PULLUP_CONTROL 1 40 | #define USB_PULLUP_GPIO_PORT GPIOB 41 | #define USB_PULLUP_GPIO_PIN GPIO9 42 | #define USB_PULLUP_ACTIVE_HIGH 0 43 | #define USB_PULLUP_OPEN_DRAIN 1 44 | 45 | #define USES_GPIOA 0 46 | #define USES_GPIOB 1 47 | #define USES_GPIOC 0 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/stm32f103/pxt32/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Devan Lai 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software 5 | * for any purpose with or without fee is hereby granted, provided 6 | * that the above copyright notice and this permission notice 7 | * appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 13 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 15 | * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef CONFIG_H_INCLUDED 20 | #define CONFIG_H_INCLUDED 21 | 22 | #define APP_BASE_ADDRESS 0x08004000 23 | #define FLASH_SIZE_OVERRIDE (512*1024) 24 | #define FLASH_PAGE_SIZE 2048 25 | #define DFU_UPLOAD_AVAILABLE 1 26 | #define DFU_DOWNLOAD_AVAILABLE 1 27 | 28 | #ifndef HAVE_LED 29 | #define HAVE_LED 0 30 | #endif 31 | 32 | #ifndef HAVE_BUTTON 33 | #define HAVE_BUTTON 0 34 | #endif 35 | 36 | #ifndef HAVE_USB_PULLUP_CONTROL 37 | #define HAVE_USB_PULLUP_CONTROL 0 38 | #endif 39 | 40 | #define UF2_FAMILY 0x5ee21072 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/stm32f103/stlink/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Devan Lai 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software 5 | * for any purpose with or without fee is hereby granted, provided 6 | * that the above copyright notice and this permission notice 7 | * appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 13 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 15 | * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef CONFIG_H_INCLUDED 20 | #define CONFIG_H_INCLUDED 21 | 22 | #define APP_BASE_ADDRESS 0x08004000 23 | #define FLASH_SIZE_OVERRIDE 0x20000 24 | #define FLASH_PAGE_SIZE 1024 25 | #define DFU_UPLOAD_AVAILABLE 1 26 | #define DFU_DOWNLOAD_AVAILABLE 1 27 | 28 | #define HAVE_LED 1 29 | #define LED_GPIO_PORT GPIOA 30 | #define LED_GPIO_PIN GPIO9 31 | #define LED_OPEN_DRAIN 0 32 | 33 | #define HAVE_BUTTON 0 34 | 35 | #define HAVE_USB_PULLUP_CONTROL 0 36 | 37 | #define USES_GPIOA 1 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/stm32f103/stm32f103x8.ld: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the libopencm3 project. 3 | * 4 | * Copyright (C) 2015 Karl Palsson 5 | * 6 | * This library is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this library. If not, see . 18 | */ 19 | 20 | /* Linker script for STM32F103x8, 64k flash, 20k RAM. */ 21 | 22 | /* Define memory regions. */ 23 | /* 8k for the bootloader */ 24 | MEMORY 25 | { 26 | rom (rx) : ORIGIN = 0x08000000, LENGTH = 16K 27 | ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K 28 | } 29 | 30 | /* Include the common ld script. */ 31 | INCLUDE libopencm3_stm32f1.ld 32 | -------------------------------------------------------------------------------- /src/stm32f103/target_stm32f103.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Devan Lai 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software 5 | * for any purpose with or without fee is hereby granted, provided 6 | * that the above copyright notice and this permission notice 7 | * appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 13 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 15 | * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* Common STM32F103 target functions */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "target.h" 29 | #include "config.h" 30 | #include "backup.h" 31 | 32 | #ifndef USES_GPIOA 33 | #if (HAVE_USB_PULLUP_CONTROL == 0) 34 | #define USES_GPIOA 1 35 | #else 36 | #define USES_GPIOA 0 37 | #endif 38 | #endif 39 | 40 | #ifndef USES_GPIOB 41 | #define USES_GPIOB 0 42 | #endif 43 | 44 | #ifndef USES_GPIOC 45 | #define USES_GPIOC 0 46 | #endif 47 | 48 | #ifdef FLASH_SIZE_OVERRIDE 49 | _Static_assert((FLASH_BASE + FLASH_SIZE_OVERRIDE >= APP_BASE_ADDRESS), 50 | "Incompatible flash size"); 51 | #endif 52 | 53 | static const uint32_t CMD_BOOT = 0x544F4F42UL; 54 | static const uint32_t CMD_APP = 0x3f82722aUL; 55 | 56 | //#define USE_HSI 1 57 | 58 | void target_clock_setup(void) { 59 | #ifdef USE_HSI 60 | /* Set the system clock to 48MHz from the internal RC oscillator. 61 | The clock tolerance doesn't meet the official USB spec, but 62 | it's better than nothing. */ 63 | rcc_clock_setup_in_hsi_out_48mhz(); 64 | #else 65 | /* Set system clock to 72 MHz from an external crystal */ 66 | #ifdef CRYSTAL_16MHZ 67 | rcc_clock_setup_in_hse_16mhz_out_72mhz(); 68 | #else 69 | rcc_clock_setup_in_hse_8mhz_out_72mhz(); 70 | #endif 71 | #endif 72 | } 73 | 74 | void target_set_led(int on) { 75 | #if HAVE_LED 76 | if ((on && LED_OPEN_DRAIN) || (!on && !LED_OPEN_DRAIN)) { 77 | gpio_clear(LED_GPIO_PORT, LED_GPIO_PIN); 78 | } else { 79 | gpio_set(LED_GPIO_PORT, LED_GPIO_PIN); 80 | } 81 | #else 82 | (void)on; 83 | #endif 84 | } 85 | 86 | static void sleep_us(int us){ 87 | for (int i = 0; i < us*10; i++) { 88 | __asm__("nop"); 89 | } 90 | } 91 | 92 | void target_gpio_setup(void) { 93 | /* Enable GPIO clocks */ 94 | rcc_periph_clock_enable(RCC_GPIOA); 95 | rcc_periph_clock_enable(RCC_GPIOB); 96 | rcc_periph_clock_enable(RCC_GPIOC); 97 | 98 | /* Setup LEDs */ 99 | #if HAVE_LED 100 | { 101 | const uint8_t mode = GPIO_MODE_OUTPUT_10_MHZ; 102 | const uint8_t conf = (LED_OPEN_DRAIN ? GPIO_CNF_OUTPUT_OPENDRAIN 103 | : GPIO_CNF_OUTPUT_PUSHPULL); 104 | if (LED_OPEN_DRAIN) { 105 | gpio_set(LED_GPIO_PORT, LED_GPIO_PIN); 106 | } else { 107 | gpio_clear(LED_GPIO_PORT, LED_GPIO_PIN); 108 | } 109 | gpio_set_mode(LED_GPIO_PORT, mode, conf, LED_GPIO_PIN); 110 | } 111 | #endif 112 | 113 | /* Setup the internal pull-up/pull-down for the button */ 114 | #if HAVE_BUTTON 115 | { 116 | const uint8_t mode = GPIO_MODE_INPUT; 117 | const uint8_t conf = GPIO_CNF_INPUT_PULL_UPDOWN; 118 | gpio_set_mode(BUTTON_GPIO_PORT, mode, conf, BUTTON_GPIO_PIN); 119 | if (BUTTON_ACTIVE_HIGH) { 120 | gpio_clear(BUTTON_GPIO_PORT, BUTTON_GPIO_PIN); 121 | } else { 122 | gpio_set(BUTTON_GPIO_PORT, BUTTON_GPIO_PIN); 123 | } 124 | } 125 | #endif 126 | 127 | #if HAVE_USB_PULLUP_CONTROL 128 | { 129 | const uint8_t mode = GPIO_MODE_OUTPUT_10_MHZ; 130 | const uint8_t conf = (USB_PULLUP_OPEN_DRAIN ? GPIO_CNF_OUTPUT_OPENDRAIN 131 | : GPIO_CNF_OUTPUT_PUSHPULL); 132 | /* Configure USB pullup transistor, initially disabled */ 133 | if (USB_PULLUP_ACTIVE_HIGH) { 134 | gpio_clear(USB_PULLUP_GPIO_PORT, USB_PULLUP_GPIO_PIN); 135 | } else { 136 | gpio_set(USB_PULLUP_GPIO_PORT, USB_PULLUP_GPIO_PIN); 137 | } 138 | gpio_set_mode(USB_PULLUP_GPIO_PORT, mode, conf, USB_PULLUP_GPIO_PIN); 139 | } 140 | #else 141 | { 142 | /* Drive the USB DP pin to override the pull-up */ 143 | gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_10_MHZ, 144 | GPIO_CNF_OUTPUT_PUSHPULL, GPIO12); 145 | } 146 | #endif 147 | 148 | #if 0 149 | while(1) { 150 | target_set_led(1); 151 | sleep_us(1200000); 152 | target_set_led(0); 153 | sleep_us(1200000); 154 | } 155 | 156 | // TFT 157 | // RST 158 | gpio_clear(GPIOC, (1 << 4)); 159 | sleep_us(20000); 160 | //gpio_set(GPIOC, (1 << 4)); 161 | sleep_us(20000); 162 | #endif 163 | } 164 | 165 | const usbd_driver* target_usb_init(void) { 166 | rcc_periph_reset_pulse(RST_USB); 167 | 168 | #if HAVE_USB_PULLUP_CONTROL 169 | /* Enable USB pullup to connect */ 170 | if (USB_PULLUP_ACTIVE_HIGH) { 171 | gpio_set(USB_PULLUP_GPIO_PORT, USB_PULLUP_GPIO_PIN); 172 | } else { 173 | gpio_clear(USB_PULLUP_GPIO_PORT, USB_PULLUP_GPIO_PIN); 174 | } 175 | #else 176 | /* Override hard-wired USB pullup to disconnect and reconnect */ 177 | gpio_clear(GPIOA, GPIO12); 178 | int i; 179 | for (i = 0; i < 800000; i++) { 180 | __asm__("nop"); 181 | } 182 | #endif 183 | 184 | return &st_usbfs_v1_usb_driver; 185 | } 186 | 187 | void target_manifest_app(void) { 188 | backup_write(BKP0, CMD_APP); 189 | scb_reset_system(); 190 | } 191 | 192 | bool target_get_force_app(void) { 193 | if (backup_read(BKP0) == CMD_APP) { 194 | backup_write(BKP0, 0); 195 | return true; 196 | } 197 | return false; 198 | } 199 | 200 | bool target_get_force_bootloader(void) { 201 | /* Enable GPIO clocks */ 202 | rcc_periph_clock_enable(RCC_GPIOA); 203 | rcc_periph_clock_enable(RCC_GPIOB); 204 | rcc_periph_clock_enable(RCC_GPIOC); 205 | 206 | bool force = true; 207 | /* Check the RTC backup register */ 208 | uint32_t cmd = backup_read(BKP0); 209 | if (cmd == CMD_BOOT) { 210 | // asked to go into bootloader? 211 | backup_write(BKP0, 0); 212 | return true; 213 | } 214 | if (cmd == CMD_APP) { 215 | // we were told to reset into app 216 | backup_write(BKP0, 0); 217 | return false; 218 | } 219 | 220 | #ifdef DOUBLE_TAP 221 | target_set_led(1); 222 | // wait for second press on reset 223 | backup_write(BKP0, CMD_BOOT); 224 | for (int i = 0; i < 3500000; ++i) 225 | asm("nop"); 226 | backup_write(BKP0, 0); 227 | target_set_led(0); 228 | force = false; 229 | #else 230 | // a reset now should go into app 231 | backup_write(BKP0, CMD_APP); 232 | #endif 233 | 234 | #if HAVE_BUTTON 235 | /* Check if the user button is held down */ 236 | if (BUTTON_ACTIVE_HIGH) { 237 | if (gpio_get(BUTTON_GPIO_PORT, BUTTON_GPIO_PIN)) { 238 | force = true; 239 | } 240 | } else { 241 | if (!gpio_get(BUTTON_GPIO_PORT, BUTTON_GPIO_PIN)) { 242 | force = true; 243 | } 244 | } 245 | #endif 246 | 247 | return force; 248 | } 249 | 250 | void target_get_serial_number(char* dest, size_t max_chars) { 251 | desig_get_unique_id_as_string(dest, max_chars+1); 252 | } 253 | 254 | static uint16_t* get_flash_end(void) { 255 | #ifdef FLASH_SIZE_OVERRIDE 256 | /* Allow access to the unofficial full 128KiB flash size */ 257 | return (uint16_t*)(FLASH_BASE + FLASH_SIZE_OVERRIDE); 258 | #else 259 | /* Only allow access to the chip's self-reported flash size */ 260 | return (uint16_t*)(FLASH_BASE + (size_t)DESIG_FLASH_SIZE*FLASH_PAGE_SIZE); 261 | #endif 262 | } 263 | 264 | size_t target_get_max_firmware_size(void) { 265 | uint8_t* flash_end = (uint8_t*)get_flash_end(); 266 | uint8_t* flash_start = (uint8_t*)(APP_BASE_ADDRESS); 267 | 268 | return (flash_end >= flash_start) ? (size_t)(flash_end - flash_start) : 0; 269 | } 270 | 271 | void target_relocate_vector_table(void) { 272 | SCB_VTOR = APP_BASE_ADDRESS & 0xFFFF; 273 | } 274 | 275 | void target_flash_unlock(void) { 276 | flash_unlock(); 277 | } 278 | 279 | void target_flash_lock(void) { 280 | flash_lock(); 281 | } 282 | 283 | static inline uint16_t* get_flash_page_address(uint16_t* dest) { 284 | return (uint16_t*)(((uint32_t)dest / FLASH_PAGE_SIZE) * FLASH_PAGE_SIZE); 285 | } 286 | 287 | bool target_flash_program_array(uint16_t* dest, const uint16_t* data, size_t half_word_count) { 288 | bool verified = true; 289 | 290 | /* Remember the bounds of erased data in the current page */ 291 | static uint16_t* erase_start; 292 | static uint16_t* erase_end; 293 | 294 | const uint16_t* flash_end = get_flash_end(); 295 | while (half_word_count > 0) { 296 | /* Avoid writing past the end of flash */ 297 | if (dest >= flash_end) { 298 | verified = false; 299 | break; 300 | } 301 | 302 | if (dest >= erase_end || dest < erase_start) { 303 | erase_start = get_flash_page_address(dest); 304 | erase_end = erase_start + (FLASH_PAGE_SIZE)/sizeof(uint16_t); 305 | flash_erase_page((uint32_t)erase_start); 306 | } 307 | flash_program_half_word((uint32_t)dest, *data); 308 | erase_start = dest + 1; 309 | if (*dest != *data) { 310 | verified = false; 311 | break; 312 | } 313 | dest++; 314 | data++; 315 | half_word_count--; 316 | } 317 | 318 | return verified; 319 | } 320 | -------------------------------------------------------------------------------- /src/target.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Devan Lai 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software 5 | * for any purpose with or without fee is hereby granted, provided 6 | * that the above copyright notice and this permission notice 7 | * appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 13 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 15 | * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef TARGET_H_INCLUDED 20 | #define TARGET_H_INCLUDED 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | extern void target_clock_setup(void); 27 | extern void target_gpio_setup(void); 28 | extern const usbd_driver* target_usb_init(void); 29 | extern bool target_get_force_bootloader(void); 30 | extern bool target_get_force_app(void); 31 | extern void target_get_serial_number(char* dest, size_t max_chars); 32 | extern size_t target_get_max_firmware_size(void); 33 | extern void target_log(const char* str); 34 | extern void target_relocate_vector_table(void); 35 | extern void target_manifest_app(void); 36 | extern void target_flash_unlock(void); 37 | extern void target_flash_lock(void); 38 | extern bool target_flash_program_array(uint16_t* dest, const uint16_t* data, size_t half_word_count); 39 | extern void target_set_led(int on); 40 | 41 | extern void target_pre_main(void); 42 | #endif 43 | -------------------------------------------------------------------------------- /src/targets.mk: -------------------------------------------------------------------------------- 1 | ## Copyright (c) 2016, Devan Lai 2 | ## 3 | ## Permission to use, copy, modify, and/or distribute this software 4 | ## for any purpose with or without fee is hereby granted, provided 5 | ## that the above copyright notice and this permission notice 6 | ## appear in all copies. 7 | ## 8 | ## THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 9 | ## WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 10 | ## WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 11 | ## AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 12 | ## CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 | ## LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 14 | ## NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 15 | ## CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | ifeq ($(TARGET),STM32F103) 18 | TARGET_COMMON_DIR := ./stm32f103 19 | TARGET_SPEC_DIR := ./stm32f103/generic 20 | LDSCRIPT := ./stm32f103/stm32f103x8.ld 21 | ARCH = STM32F1 22 | endif 23 | ifeq ($(TARGET),BLUEPILL) 24 | TARGET_COMMON_DIR := ./stm32f103 25 | TARGET_SPEC_DIR := ./stm32f103/bluepill 26 | LDSCRIPT := ./stm32f103/stm32f103x8.ld 27 | ARCH = STM32F1 28 | DEFS += -DHAVE_LED=1 -DLED_GPIO_PORT=GPIOC -DLED_GPIO_PIN=GPIO13 -DLED_OPEN_DRAIN=1 -DUSES_GPIOC=1 29 | endif 30 | ifeq ($(TARGET),MAPLEMINI) 31 | TARGET_COMMON_DIR := ./stm32f103 32 | TARGET_SPEC_DIR := ./stm32f103/maplemini 33 | LDSCRIPT := ./stm32f103/stm32f103x8.ld 34 | ARCH = STM32F1 35 | endif 36 | ifeq ($(TARGET),STLINK) 37 | TARGET_COMMON_DIR := ./stm32f103 38 | TARGET_SPEC_DIR := ./stm32f103/stlink 39 | LDSCRIPT := ./stm32f103/stm32f103x8.ld 40 | ARCH = STM32F1 41 | endif 42 | ifeq ($(TARGET),PXT32) 43 | TARGET_COMMON_DIR := ./stm32f103 44 | TARGET_SPEC_DIR := ./stm32f103/pxt32 45 | LDSCRIPT := ./stm32f103/stm32f103x8.ld 46 | ARCH = STM32F1 47 | DEFS += -DHAVE_LED=1 -DLED_GPIO_PORT=GPIOB -DLED_GPIO_PIN=GPIO11 -DLED_OPEN_DRAIN=1 -DUSES_GPIOB=1 48 | endif 49 | ifeq ($(TARGET),JACDAC) 50 | TARGET_COMMON_DIR := ./stm32f103 51 | TARGET_SPEC_DIR := ./stm32f103/jacdac 52 | LDSCRIPT := ./stm32f103/stm32f103x8.ld 53 | ARCH = STM32F1 54 | DEFS += -DHAVE_LED=1 -DLED_GPIO_PORT=GPIOB -DLED_GPIO_PIN=GPIO13 -DLED_OPEN_DRAIN=0 55 | endif 56 | 57 | ifndef ARCH 58 | $(error Unknown target $(TARGET)) 59 | endif 60 | -------------------------------------------------------------------------------- /src/uf2.h: -------------------------------------------------------------------------------- 1 | #ifndef UF2FORMAT_H 2 | #define UF2FORMAT_H 1 3 | 4 | #include "uf2cfg.h" 5 | #include "target.h" 6 | #include "config.h" 7 | 8 | #include 9 | #include 10 | 11 | // All entries are little endian. 12 | 13 | #define UF2_MAGIC_START0 0x0A324655UL // "UF2\n" 14 | #define UF2_MAGIC_START1 0x9E5D5157UL // Randomly selected 15 | #define UF2_MAGIC_END 0x0AB16F30UL // Ditto 16 | 17 | // If set, the block is "comment" and should not be flashed to the device 18 | #define UF2_FLAG_NOFLASH 0x00000001 19 | #define UF2_FLAG_FAMILYID_PRESENT 0x00002000 20 | 21 | #define UF2_IS_MY_FAMILY(bl) \ 22 | (((bl)->flags & UF2_FLAG_FAMILYID_PRESENT) == 0 || (bl)->familyID == UF2_FAMILY) 23 | 24 | #define MAX_BLOCKS (FLASH_SIZE_OVERRIDE / 256 + 100) 25 | typedef struct { 26 | uint32_t numBlocks; 27 | uint32_t numWritten; 28 | uint8_t writtenMask[MAX_BLOCKS / 8 + 1]; 29 | } WriteState; 30 | 31 | typedef struct { 32 | // 32 byte header 33 | uint32_t magicStart0; 34 | uint32_t magicStart1; 35 | uint32_t flags; 36 | uint32_t targetAddr; 37 | uint32_t payloadSize; 38 | uint32_t blockNo; 39 | uint32_t numBlocks; 40 | uint32_t familyID; 41 | 42 | // raw data; 43 | uint8_t data[476]; 44 | 45 | // store magic also at the end to limit damage from partial block reads 46 | uint32_t magicEnd; 47 | } UF2_Block; 48 | 49 | typedef struct { 50 | uint8_t version; 51 | uint8_t ep_in; 52 | uint8_t ep_out; 53 | uint8_t reserved0; 54 | uint32_t cbw_tag; 55 | uint32_t blocks_remaining; 56 | uint8_t *buffer; 57 | } UF2_HandoverArgs; 58 | 59 | int write_block(uint32_t lba, const uint8_t *copy_from); 60 | int read_block(uint32_t block_no, uint8_t *data); 61 | void ghostfat_1ms(void); 62 | 63 | typedef void (*UF2_MSC_Handover_Handler)(UF2_HandoverArgs *handover); 64 | typedef void (*UF2_HID_Handover_Handler)(int ep); 65 | 66 | // this is required to be exactly 16 bytes long by the linker script 67 | typedef struct { 68 | void *reserved0; 69 | UF2_HID_Handover_Handler handoverHID; 70 | UF2_MSC_Handover_Handler handoverMSC; 71 | const char *info_uf2; 72 | } UF2_BInfo; 73 | 74 | #define UF2_BINFO ((UF2_BInfo *)(APP_START_ADDRESS - sizeof(UF2_BInfo))) 75 | 76 | static inline bool is_uf2_block(const void *data) { 77 | const UF2_Block *bl = (const UF2_Block *)data; 78 | return bl->magicStart0 == UF2_MAGIC_START0 && bl->magicStart1 == UF2_MAGIC_START1 && 79 | bl->magicEnd == UF2_MAGIC_END; 80 | } 81 | 82 | static inline bool in_uf2_bootloader_space(const void *addr) { 83 | return USER_FLASH_END <= (uint32_t)addr && (uint32_t)addr < FLASH_SIZE_OVERRIDE; 84 | } 85 | 86 | 87 | #ifdef UF2_DEFINE_HANDOVER 88 | static inline const char *uf2_info(void) { 89 | if (in_uf2_bootloader_space(UF2_BINFO->info_uf2)) 90 | return UF2_BINFO->info_uf2; 91 | return "N/A"; 92 | } 93 | 94 | static inline void hf2_handover(uint8_t ep) { 95 | const char *board_info = UF2_BINFO->info_uf2; 96 | UF2_HID_Handover_Handler fn = UF2_BINFO->handoverHID; 97 | 98 | if (in_uf2_bootloader_space(board_info) && in_uf2_bootloader_space((const void *)fn) && 99 | ((uint32_t)fn & 1)) { 100 | // Pass control to bootloader; never returns 101 | fn(ep & 0xf); 102 | } 103 | } 104 | 105 | // the ep_in/ep_out are without the 0x80 mask 106 | // cbw_tag is in the same bit format as it came 107 | static inline void check_uf2_handover(uint8_t *buffer, uint32_t blocks_remaining, uint8_t ep_in, 108 | uint8_t ep_out, uint32_t cbw_tag) { 109 | if (!is_uf2_block(buffer)) 110 | return; 111 | 112 | const char *board_info = UF2_BINFO->info_uf2; 113 | UF2_MSC_Handover_Handler fn = UF2_BINFO->handoverMSC; 114 | 115 | if (in_uf2_bootloader_space(board_info) && in_uf2_bootloader_space((const void *)fn) && 116 | ((uint32_t)fn & 1)) { 117 | UF2_HandoverArgs hand = { 118 | 1, ep_in, ep_out, 0, cbw_tag, blocks_remaining, buffer, 119 | }; 120 | // Pass control to bootloader; never returns 121 | fn(&hand); 122 | } 123 | } 124 | #endif 125 | 126 | #endif 127 | -------------------------------------------------------------------------------- /src/uf2cfg.h: -------------------------------------------------------------------------------- 1 | #define PRODUCT_NAME "STM32 Board" 2 | #define BOARD_ID "STM32F103-generic-v0" 3 | #define INDEX_URL "https://maker.makecode.com" 4 | #define UF2_NUM_BLOCKS 8000 5 | #define VOLUME_LABEL "STM32" 6 | // where the UF2 files are allowed to write data - we allow MBR, since it seems part of the softdevice .hex file 7 | #define USER_FLASH_START (uint32_t)(APP_BASE_ADDRESS) 8 | #define USER_FLASH_END (0x08000000+FLASH_SIZE_OVERRIDE) 9 | -------------------------------------------------------------------------------- /src/usb_conf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Devan Lai 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software 5 | * for any purpose with or without fee is hereby granted, provided 6 | * that the above copyright notice and this permission notice 7 | * appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 13 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 15 | * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "target.h" 24 | #include "webusb.h" 25 | 26 | #include 27 | 28 | #include "usb_conf.h" 29 | 30 | static const struct usb_device_descriptor dev = { 31 | .bLength = USB_DT_DEVICE_SIZE, 32 | .bDescriptorType = USB_DT_DEVICE, 33 | .bcdUSB = 0x0210, 34 | .bDeviceClass = 0, 35 | .bDeviceSubClass = 0, 36 | .bDeviceProtocol = 0, 37 | .bMaxPacketSize0 = 64, 38 | .idVendor = USB_VID, 39 | .idProduct = USB_PID, 40 | .bcdDevice = 0x0110, 41 | .iManufacturer = 1, 42 | .iProduct = 2, 43 | .iSerialNumber = 3, 44 | .bNumConfigurations = 1, 45 | }; 46 | 47 | static const struct usb_interface_descriptor dfu_iface = { 48 | .bLength = USB_DT_INTERFACE_SIZE, 49 | .bDescriptorType = USB_DT_INTERFACE, 50 | .bInterfaceNumber = INTF_DFU, 51 | .bAlternateSetting = 0, 52 | .bNumEndpoints = 0, 53 | .bInterfaceClass = 0xFE, 54 | .bInterfaceSubClass = 1, 55 | .bInterfaceProtocol = 2, 56 | .iInterface = 4, 57 | 58 | .endpoint = NULL, 59 | }; 60 | 61 | static const struct usb_endpoint_descriptor msc_endp[] = {{ 62 | .bLength = USB_DT_ENDPOINT_SIZE, 63 | .bDescriptorType = USB_DT_ENDPOINT, 64 | .bEndpointAddress = 0x01, 65 | .bmAttributes = USB_ENDPOINT_ATTR_BULK, 66 | .wMaxPacketSize = 64, 67 | .bInterval = 0, 68 | }, { 69 | .bLength = USB_DT_ENDPOINT_SIZE, 70 | .bDescriptorType = USB_DT_ENDPOINT, 71 | .bEndpointAddress = 0x82, 72 | .bmAttributes = USB_ENDPOINT_ATTR_BULK, 73 | .wMaxPacketSize = 64, 74 | .bInterval = 0, 75 | }}; 76 | 77 | static const struct usb_interface_descriptor msc_iface = { 78 | .bLength = USB_DT_INTERFACE_SIZE, 79 | .bDescriptorType = USB_DT_INTERFACE, 80 | .bInterfaceNumber = INTF_MSC, 81 | .bAlternateSetting = 0, 82 | .bNumEndpoints = 2, 83 | .bInterfaceClass = USB_CLASS_MSC, 84 | .bInterfaceSubClass = USB_MSC_SUBCLASS_SCSI, 85 | .bInterfaceProtocol = USB_MSC_PROTOCOL_BBB, 86 | .iInterface = 0, 87 | .endpoint = msc_endp, 88 | .extra = NULL, 89 | .extralen = 0 90 | }; 91 | 92 | static const struct usb_interface interfaces[] = { 93 | /* DFU interface */ 94 | { 95 | .num_altsetting = 1, 96 | .altsetting = &dfu_iface, 97 | }, 98 | { 99 | .num_altsetting = 1, 100 | .altsetting = &msc_iface, 101 | }, 102 | }; 103 | 104 | static const struct usb_config_descriptor config = { 105 | .bLength = USB_DT_CONFIGURATION_SIZE, 106 | .bDescriptorType = USB_DT_CONFIGURATION, 107 | .wTotalLength = 0, 108 | .bNumInterfaces = sizeof(interfaces)/sizeof(struct usb_interface), 109 | .bConfigurationValue = 1, 110 | .iConfiguration = 0, 111 | .bmAttributes = 0xC0, 112 | .bMaxPower = 0x32, 113 | 114 | .interface = interfaces, 115 | }; 116 | 117 | static const struct usb_device_capability_descriptor* capabilities[] = { 118 | (const struct usb_device_capability_descriptor*)&webusb_platform, 119 | }; 120 | 121 | static const struct usb_bos_descriptor bos = { 122 | .bLength = USB_DT_BOS_SIZE, 123 | .bDescriptorType = USB_DT_BOS, 124 | .wTotalLength = USB_DT_BOS_SIZE + sizeof(webusb_platform), 125 | .bNumDeviceCaps = sizeof(capabilities)/sizeof(capabilities[0]), 126 | .capabilities = capabilities 127 | }; 128 | 129 | static char serial_number[USB_SERIAL_NUM_LENGTH+1]; 130 | 131 | static const char *usb_strings[] = { 132 | "Devanarchy", 133 | "DAPBoot DFU Bootloader", 134 | serial_number, 135 | "DAPBoot DFU" 136 | }; 137 | 138 | /* Buffer to be used for control requests. */ 139 | static uint8_t usbd_control_buffer[USB_CONTROL_BUF_SIZE] __attribute__ ((aligned (2))); 140 | 141 | void usb_set_serial_number(const char* serial) { 142 | serial_number[0] = '\0'; 143 | if (serial) { 144 | strncpy(serial_number, serial, USB_SERIAL_NUM_LENGTH); 145 | serial_number[USB_SERIAL_NUM_LENGTH] = '\0'; 146 | } 147 | } 148 | 149 | usbd_device* usb_setup(void) { 150 | int num_strings = sizeof(usb_strings)/sizeof(const char*); 151 | 152 | const usbd_driver* driver = target_usb_init(); 153 | usbd_device* usbd_dev = usbd_init(driver, &dev, &config, &bos, 154 | usb_strings, num_strings, 155 | usbd_control_buffer, sizeof(usbd_control_buffer)); 156 | 157 | return usbd_dev; 158 | } 159 | -------------------------------------------------------------------------------- /src/usb_conf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Devan Lai 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software 5 | * for any purpose with or without fee is hereby granted, provided 6 | * that the above copyright notice and this permission notice 7 | * appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 13 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 15 | * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef USB_CONF_H_INCLUDED 20 | #define USB_CONF_H_INCLUDED 21 | 22 | #include 23 | 24 | #define USB_VID 0x1209 25 | #define USB_PID 0xdb42 26 | #define USB_CONTROL_BUF_SIZE 1024 27 | #define USB_SERIAL_NUM_LENGTH 24 28 | #define INTF_DFU 0 29 | #define INTF_MSC 1 30 | 31 | extern void usb_set_serial_number(const char* serial); 32 | extern usbd_device* usb_setup(void); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/usb_msc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the libopencm3 project. 3 | * 4 | * Copyright (C) 2013 Weston Schmidt 5 | * Copyright (C) 2013 Pavol Rusnak 6 | * 7 | * This library is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU Lesser General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU Lesser General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Lesser General Public License 18 | * along with this library. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "../lib/usb/usb_private.h" 28 | 29 | #include "dmesg.h" 30 | // #define LOG DMESG 31 | #define LOG NOOP 32 | 33 | /* Definitions of Mass Storage Class from: 34 | * 35 | * (A) "Universal Serial Bus Mass Storage Class Bulk-Only Transport 36 | * Revision 1.0" 37 | * 38 | * (B) "Universal Serial Bus Mass Storage Class Specification Overview 39 | * Revision 1.0" 40 | */ 41 | 42 | /* Command Block Wrapper */ 43 | #define CBW_SIGNATURE 0x43425355 44 | #define CBW_STATUS_SUCCESS 0 45 | #define CBW_STATUS_FAILED 1 46 | #define CBW_STATUS_PHASE_ERROR 2 47 | 48 | /* Command Status Wrapper */ 49 | #define CSW_SIGNATURE 0x53425355 50 | #define CSW_STATUS_SUCCESS 0 51 | #define CSW_STATUS_FAILED 1 52 | #define CSW_STATUS_PHASE_ERROR 2 53 | 54 | /* Implemented SCSI Commands */ 55 | #define SCSI_TEST_UNIT_READY 0x00 56 | #define SCSI_REQUEST_SENSE 0x03 57 | #define SCSI_FORMAT_UNIT 0x04 58 | #define SCSI_READ_6 0x08 59 | #define SCSI_WRITE_6 0x0A 60 | #define SCSI_INQUIRY 0x12 61 | #define SCSI_MODE_SENSE_6 0x1A 62 | #define SCSI_SEND_DIAGNOSTIC 0x1D 63 | #define SCSI_READ_CAPACITY 0x25 64 | #define SCSI_READ_10 0x28 65 | 66 | 67 | /* Required SCSI Commands */ 68 | 69 | /* Optional SCSI Commands */ 70 | #define SCSI_REPORT_LUNS 0xA0 71 | #define SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1E 72 | #define SCSI_MODE_SELECT_6 0x15 73 | #define SCSI_MODE_SELECT_10 0x55 74 | #define SCSI_MODE_SENSE_10 0x5A 75 | #define SCSI_READ_12 0xA8 76 | #define SCSI_READ_FORMAT_CAPACITIES 0x23 77 | #define SCSI_READ_TOC_PMA_ATIP 0x43 78 | #define SCSI_START_STOP_UNIT 0x1B 79 | #define SCSI_SYNCHRONIZE_CACHE 0x35 80 | #define SCSI_VERIFY 0x2F 81 | #define SCSI_WRITE_10 0x2A 82 | #define SCSI_WRITE_12 0xAA 83 | 84 | /* The sense codes */ 85 | enum sbc_sense_key { 86 | SBC_SENSE_KEY_NO_SENSE = 0x00, 87 | SBC_SENSE_KEY_RECOVERED_ERROR = 0x01, 88 | SBC_SENSE_KEY_NOT_READY = 0x02, 89 | SBC_SENSE_KEY_MEDIUM_ERROR = 0x03, 90 | SBC_SENSE_KEY_HARDWARE_ERROR = 0x04, 91 | SBC_SENSE_KEY_ILLEGAL_REQUEST = 0x05, 92 | SBC_SENSE_KEY_UNIT_ATTENTION = 0x06, 93 | SBC_SENSE_KEY_DATA_PROTECT = 0x07, 94 | SBC_SENSE_KEY_BLANK_CHECK = 0x08, 95 | SBC_SENSE_KEY_VENDOR_SPECIFIC = 0x09, 96 | SBC_SENSE_KEY_COPY_ABORTED = 0x0A, 97 | SBC_SENSE_KEY_ABORTED_COMMAND = 0x0B, 98 | SBC_SENSE_KEY_VOLUME_OVERFLOW = 0x0D, 99 | SBC_SENSE_KEY_MISCOMPARE = 0x0E 100 | }; 101 | 102 | enum sbc_asc { 103 | SBC_ASC_NO_ADDITIONAL_SENSE_INFORMATION = 0x00, 104 | SBC_ASC_PERIPHERAL_DEVICE_WRITE_FAULT = 0x03, 105 | SBC_ASC_LOGICAL_UNIT_NOT_READY = 0x04, 106 | SBC_ASC_UNRECOVERED_READ_ERROR = 0x11, 107 | SBC_ASC_INVALID_COMMAND_OPERATION_CODE = 0x20, 108 | SBC_ASC_LBA_OUT_OF_RANGE = 0x21, 109 | SBC_ASC_INVALID_FIELD_IN_CDB = 0x24, 110 | SBC_ASC_WRITE_PROTECTED = 0x27, 111 | SBC_ASC_NOT_READY_TO_READY_CHANGE = 0x28, 112 | SBC_ASC_FORMAT_ERROR = 0x31, 113 | SBC_ASC_MEDIUM_NOT_PRESENT = 0x3A 114 | }; 115 | 116 | enum sbc_ascq { 117 | SBC_ASCQ_NA = 0x00, 118 | SBC_ASCQ_FORMAT_COMMAND_FAILED = 0x01, 119 | SBC_ASCQ_INITIALIZING_COMMAND_REQUIRED = 0x02, 120 | SBC_ASCQ_OPERATION_IN_PROGRESS = 0x07 121 | }; 122 | 123 | enum trans_event { 124 | EVENT_CBW_VALID, 125 | EVENT_NEED_STATUS 126 | }; 127 | 128 | struct usb_msc_cbw { 129 | uint32_t dCBWSignature; 130 | uint32_t dCBWTag; 131 | uint32_t dCBWDataTransferLength; 132 | uint8_t bmCBWFlags; 133 | uint8_t bCBWLUN; 134 | uint8_t bCBWCBLength; 135 | uint8_t CBWCB[16]; 136 | } __attribute__((packed)); 137 | 138 | struct usb_msc_csw { 139 | uint32_t dCSWSignature; 140 | uint32_t dCSWTag; 141 | uint32_t dCSWDataResidue; 142 | uint8_t bCSWStatus; 143 | } __attribute__((packed)); 144 | 145 | struct sbc_sense_info { 146 | uint8_t key; 147 | uint8_t asc; 148 | uint8_t ascq; 149 | }; 150 | 151 | struct usb_msc_trans { 152 | uint8_t cbw_cnt; /* Read until 31 bytes */ 153 | union { 154 | struct usb_msc_cbw cbw; 155 | uint8_t buf[1]; 156 | } cbw; 157 | 158 | uint32_t bytes_to_read; 159 | uint32_t bytes_to_write; 160 | uint32_t byte_count; /* Either read until equal to 161 | bytes_to_read or write until equal 162 | to bytes_to_write. */ 163 | uint32_t lba_start; 164 | uint32_t block_count; 165 | uint32_t current_block; 166 | 167 | uint8_t msd_buf[512]; 168 | 169 | bool csw_valid; 170 | uint8_t csw_sent; /* Write until 13 bytes */ 171 | union { 172 | struct usb_msc_csw csw; 173 | uint8_t buf[1]; 174 | } csw; 175 | }; 176 | 177 | struct _usbd_mass_storage { 178 | usbd_device *usbd_dev; 179 | uint8_t ep_in; 180 | uint8_t ep_in_size; 181 | uint8_t ep_out; 182 | uint8_t ep_out_size; 183 | 184 | const char *vendor_id; 185 | const char *product_id; 186 | const char *product_revision_level; 187 | uint32_t block_count; 188 | 189 | int (*read_block)(uint32_t lba, uint8_t *copy_to); 190 | int (*write_block)(uint32_t lba, const uint8_t *copy_from); 191 | 192 | void (*lock)(void); 193 | void (*unlock)(void); 194 | 195 | struct usb_msc_trans trans; 196 | struct sbc_sense_info sense; 197 | }; 198 | 199 | static usbd_mass_storage _mass_storage; 200 | 201 | /*-- SCSI Base Responses -----------------------------------------------------*/ 202 | 203 | static const uint8_t _spc3_inquiry_response[36] = { 204 | 0x00, /* Byte 0: Peripheral Qualifier = 0, Peripheral Device Type = 0 */ 205 | 0x80, /* Byte 1: RMB = 1, Reserved = 0 */ 206 | 0x04, /* Byte 2: Version = 0 */ 207 | 0x02, /* Byte 3: Obsolete = 0, NormACA = 0, HiSup = 0, Response Data Format = 2 */ 208 | 0x20, /* Byte 4: Additional Length (n-4) = 31 + 4 */ 209 | 0x00, /* Byte 5: SCCS = 0, ACC = 0, TPGS = 0, 3PC = 0, Reserved = 0, Protect = 0 */ 210 | 0x00, /* Byte 6: BQue = 0, EncServ = 0, VS = 0, MultiP = 0, MChngr = 0, Obsolete = 0, Addr16 = 0 */ 211 | 0x00, /* Byte 7: Obsolete = 0, Wbus16 = 0, Sync = 0, Linked = 0, CmdQue = 0, VS = 0 */ 212 | /* Byte 8 - Byte 15: Vendor Identification */ 213 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 214 | /* Byte 16 - Byte 31: Product Identification */ 215 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 216 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 217 | /* Byte 32 - Byte 35: Product Revision Level */ 218 | 0x20, 0x20, 0x20, 0x20 219 | }; 220 | 221 | static const uint8_t _spc3_request_sense[18] = { 222 | 0x70, /* Byte 0: VALID = 0, Response Code = 112 */ 223 | 0x00, /* Byte 1: Obsolete = 0 */ 224 | 0x00, /* Byte 2: Filemark = 0, EOM = 0, ILI = 0, Reserved = 0, Sense Key = 0 */ 225 | /* Byte 3 - Byte 6: Information = 0 */ 226 | 0, 0, 0, 0, 227 | 0x0a, /* Byte 7: Additional Sense Length = 10 */ 228 | /* Byte 8 - Byte 11: Command Specific Info = 0 */ 229 | 0, 0, 0, 0, 230 | 0x00, /* Byte 12: Additional Sense Code (ASC) = 0 */ 231 | 0x00, /* Byte 13: Additional Sense Code Qualifier (ASCQ) = 0 */ 232 | 0x00, /* Byte 14: Field Replaceable Unit Code (FRUC) = 0 */ 233 | 0x00, /* Byte 15: SKSV = 0, SenseKeySpecific[0] = 0 */ 234 | 0x00, /* Byte 16: SenseKeySpecific[0] = 0 */ 235 | 0x00 /* Byte 17: SenseKeySpecific[0] = 0 */ 236 | }; 237 | 238 | /*-- SCSI Layer --------------------------------------------------------------*/ 239 | 240 | static void set_sbc_status(usbd_mass_storage *ms, 241 | enum sbc_sense_key key, 242 | enum sbc_asc asc, 243 | enum sbc_ascq ascq) 244 | { 245 | LOG("SBC st %d %d %d", key, asc, ascq); 246 | ms->sense.key = (uint8_t) key; 247 | ms->sense.asc = (uint8_t) asc; 248 | ms->sense.ascq = (uint8_t) ascq; 249 | } 250 | 251 | static void set_sbc_status_good(usbd_mass_storage *ms) 252 | { 253 | set_sbc_status(ms, 254 | SBC_SENSE_KEY_NO_SENSE, 255 | SBC_ASC_NO_ADDITIONAL_SENSE_INFORMATION, 256 | SBC_ASCQ_NA); 257 | } 258 | 259 | static uint8_t *get_cbw_buf(struct usb_msc_trans *trans) 260 | { 261 | return &trans->cbw.cbw.CBWCB[0]; 262 | } 263 | 264 | static void scsi_read_6(usbd_mass_storage *ms, 265 | struct usb_msc_trans *trans, 266 | enum trans_event event) 267 | { 268 | if (EVENT_CBW_VALID == event) { 269 | uint8_t *buf; 270 | 271 | buf = get_cbw_buf(trans); 272 | 273 | trans->lba_start = (buf[2] << 8) | buf[3]; 274 | trans->block_count = buf[4]; 275 | trans->current_block = 0; 276 | 277 | /* TODO: Check the lba & block_count for range. */ 278 | 279 | /* both are in terms of 512 byte blocks, so shift by 9 */ 280 | trans->bytes_to_write = trans->block_count << 9; 281 | 282 | set_sbc_status_good(ms); 283 | } 284 | } 285 | 286 | static void scsi_write_6(usbd_mass_storage *ms, 287 | struct usb_msc_trans *trans, 288 | enum trans_event event) 289 | { 290 | (void) ms; 291 | 292 | if (EVENT_CBW_VALID == event) { 293 | uint8_t *buf; 294 | 295 | buf = get_cbw_buf(trans); 296 | 297 | trans->lba_start = ((0x1f & buf[1]) << 16) 298 | | (buf[2] << 8) | buf[3]; 299 | trans->block_count = buf[4]; 300 | trans->current_block = 0; 301 | 302 | trans->bytes_to_read = trans->block_count << 9; 303 | } 304 | } 305 | 306 | static void scsi_write_10(usbd_mass_storage *ms, 307 | struct usb_msc_trans *trans, 308 | enum trans_event event) 309 | { 310 | (void) ms; 311 | 312 | if (EVENT_CBW_VALID == event) { 313 | uint8_t *buf; 314 | 315 | buf = get_cbw_buf(trans); 316 | 317 | trans->lba_start = (buf[2] << 24) | (buf[3] << 16) | 318 | (buf[4] << 8) | buf[5]; 319 | trans->block_count = (buf[7] << 8) | buf[8]; 320 | trans->current_block = 0; 321 | 322 | trans->bytes_to_read = trans->block_count << 9; 323 | } 324 | } 325 | 326 | static void scsi_read_10(usbd_mass_storage *ms, 327 | struct usb_msc_trans *trans, 328 | enum trans_event event) 329 | { 330 | if (EVENT_CBW_VALID == event) { 331 | uint8_t *buf; 332 | 333 | buf = get_cbw_buf(trans); 334 | 335 | trans->lba_start = (buf[2] << 24) | (buf[3] << 16) 336 | | (buf[4] << 8) | buf[5]; 337 | trans->block_count = (buf[7] << 8) | buf[8]; 338 | 339 | /* TODO: Check the lba & block_count for range. */ 340 | 341 | /* both are in terms of 512 byte blocks, so shift by 9 */ 342 | trans->bytes_to_write = trans->block_count << 9; 343 | 344 | set_sbc_status_good(ms); 345 | } 346 | } 347 | 348 | static void scsi_read_capacity(usbd_mass_storage *ms, 349 | struct usb_msc_trans *trans, 350 | enum trans_event event) 351 | { 352 | if (EVENT_CBW_VALID == event) { 353 | trans->msd_buf[0] = ms->block_count >> 24; 354 | trans->msd_buf[1] = 0xff & (ms->block_count >> 16); 355 | trans->msd_buf[2] = 0xff & (ms->block_count >> 8); 356 | trans->msd_buf[3] = 0xff & ms->block_count; 357 | 358 | /* Block size: 512 */ 359 | trans->msd_buf[4] = 0; 360 | trans->msd_buf[5] = 0; 361 | trans->msd_buf[6] = 2; 362 | trans->msd_buf[7] = 0; 363 | trans->bytes_to_write = 8; 364 | set_sbc_status_good(ms); 365 | } 366 | } 367 | 368 | static void scsi_read_format_capacity(usbd_mass_storage *ms, 369 | struct usb_msc_trans *trans, 370 | enum trans_event event) 371 | { 372 | if (EVENT_CBW_VALID == event) { 373 | trans->msd_buf[0] = 0; 374 | trans->msd_buf[1] = 0; 375 | trans->msd_buf[2] = 0; 376 | trans->msd_buf[3] = 8; 377 | trans->msd_buf[4] = ms->block_count >> 24; 378 | trans->msd_buf[5] = 0xff & (ms->block_count >> 16); 379 | trans->msd_buf[6] = 0xff & (ms->block_count >> 8); 380 | trans->msd_buf[7] = 0xff & ms->block_count; 381 | trans->msd_buf[8] = 2; // formatted media 382 | trans->msd_buf[9] = 0; 383 | trans->msd_buf[10] = 512>>8; // block size 384 | trans->msd_buf[11] = 0; 385 | trans->bytes_to_write = 12; 386 | set_sbc_status_good(ms); 387 | } 388 | } 389 | 390 | static void scsi_format_unit(usbd_mass_storage *ms, 391 | struct usb_msc_trans *trans, 392 | enum trans_event event) 393 | { 394 | if (EVENT_CBW_VALID == event) { 395 | uint32_t i; 396 | 397 | memset(trans->msd_buf, 0, 512); 398 | 399 | for (i = 0; i < ms->block_count; i++) { 400 | (*ms->write_block)(i, trans->msd_buf); 401 | } 402 | 403 | set_sbc_status_good(ms); 404 | } 405 | } 406 | 407 | static void scsi_request_sense(usbd_mass_storage *ms, 408 | struct usb_msc_trans *trans, 409 | enum trans_event event) 410 | { 411 | if (EVENT_CBW_VALID == event) { 412 | uint8_t *buf; 413 | 414 | buf = &trans->cbw.cbw.CBWCB[0]; 415 | 416 | trans->bytes_to_write = buf[4]; /* allocation length */ 417 | memcpy(trans->msd_buf, _spc3_request_sense, 418 | sizeof(_spc3_request_sense)); 419 | 420 | trans->msd_buf[2] = ms->sense.key; 421 | trans->msd_buf[12] = ms->sense.asc; 422 | trans->msd_buf[13] = ms->sense.ascq; 423 | } 424 | } 425 | 426 | static void scsi_mode_sense_6(usbd_mass_storage *ms, 427 | struct usb_msc_trans *trans, 428 | enum trans_event event) 429 | { 430 | (void) ms; 431 | 432 | if (EVENT_CBW_VALID == event) { 433 | #if 0 434 | uint8_t *buf; 435 | uint8_t page_code; 436 | uint8_t allocation_length; 437 | 438 | buf = &trans->cbw.cbw.CBWCB[0]; 439 | page_code = buf[2]; 440 | allocation_length = buf[4]; 441 | 442 | if (0x1C == page_code) { /* Informational Exceptions */ 443 | #endif 444 | trans->bytes_to_write = 4; 445 | 446 | trans->msd_buf[0] = 3; /* Num bytes that follow */ 447 | trans->msd_buf[1] = 0; /* Medium Type */ 448 | trans->msd_buf[2] = 0; /* Device specific param */ 449 | trans->csw.csw.dCSWDataResidue = 4; 450 | #if 0 451 | } else if (0x01 == page_code) { /* Error recovery */ 452 | } else if (0x3F == page_code) { /* All */ 453 | } else { 454 | /* Error */ 455 | trans->csw.csw.bCSWStatus = CSW_STATUS_FAILED; 456 | set_sbc_status(ms, 457 | SBC_SENSE_KEY_ILLEGAL_REQUEST, 458 | SBC_ASC_INVALID_FIELD_IN_CDB, 459 | SBC_ASCQ_NA); 460 | } 461 | #endif 462 | } 463 | } 464 | 465 | static void scsi_inquiry(usbd_mass_storage *ms, 466 | struct usb_msc_trans *trans, 467 | enum trans_event event) 468 | { 469 | if (EVENT_CBW_VALID == event) { 470 | uint8_t evpd; 471 | uint8_t *buf; 472 | 473 | buf = get_cbw_buf(trans); 474 | evpd = 1 & buf[1]; 475 | 476 | evpd = 0; // force response always, otherwise it fails on Windows 477 | 478 | if (0 == evpd) { 479 | size_t len; 480 | trans->bytes_to_write = sizeof(_spc3_inquiry_response); 481 | memcpy(trans->msd_buf, _spc3_inquiry_response, 482 | sizeof(_spc3_inquiry_response)); 483 | 484 | len = strlen(ms->vendor_id); 485 | len = MIN(len, 8); 486 | memcpy(&trans->msd_buf[8], ms->vendor_id, len); 487 | 488 | len = strlen(ms->product_id); 489 | len = MIN(len, 16); 490 | memcpy(&trans->msd_buf[16], ms->product_id, len); 491 | 492 | len = strlen(ms->product_revision_level); 493 | len = MIN(len, 4); 494 | memcpy(&trans->msd_buf[32], ms->product_revision_level, 495 | len); 496 | 497 | trans->csw.csw.dCSWDataResidue = 498 | sizeof(_spc3_inquiry_response); 499 | 500 | set_sbc_status_good(ms); 501 | } else { 502 | /* TODO: Add VPD 0x83 support */ 503 | /* TODO: Add VPD 0x00 support */ 504 | } 505 | } 506 | } 507 | 508 | static void scsi_command(usbd_mass_storage *ms, 509 | struct usb_msc_trans *trans, 510 | enum trans_event event) 511 | { 512 | if (EVENT_CBW_VALID == event) { 513 | /* Setup the default success */ 514 | trans->csw_sent = 0; 515 | trans->csw.csw.dCSWSignature = CSW_SIGNATURE; 516 | trans->csw.csw.dCSWTag = trans->cbw.cbw.dCBWTag; 517 | trans->csw.csw.dCSWDataResidue = 0; 518 | trans->csw.csw.bCSWStatus = CSW_STATUS_SUCCESS; 519 | 520 | trans->bytes_to_write = 0; 521 | trans->bytes_to_read = 0; 522 | trans->byte_count = 0; 523 | } 524 | 525 | LOG("SCSI %x", trans->cbw.cbw.CBWCB[0]); 526 | 527 | switch (trans->cbw.cbw.CBWCB[0]) { 528 | case SCSI_TEST_UNIT_READY: 529 | case SCSI_SEND_DIAGNOSTIC: 530 | /* Do nothing, just send the success. */ 531 | set_sbc_status_good(ms); 532 | break; 533 | case SCSI_FORMAT_UNIT: 534 | scsi_format_unit(ms, trans, event); 535 | break; 536 | case SCSI_REQUEST_SENSE: 537 | scsi_request_sense(ms, trans, event); 538 | break; 539 | case SCSI_MODE_SENSE_6: 540 | scsi_mode_sense_6(ms, trans, event); 541 | break; 542 | case SCSI_READ_6: 543 | scsi_read_6(ms, trans, event); 544 | break; 545 | case SCSI_INQUIRY: 546 | scsi_inquiry(ms, trans, event); 547 | break; 548 | case SCSI_READ_CAPACITY: 549 | scsi_read_capacity(ms, trans, event); 550 | break; 551 | case SCSI_READ_FORMAT_CAPACITIES: 552 | scsi_read_format_capacity(ms, trans, event); 553 | break; 554 | case SCSI_READ_10: 555 | scsi_read_10(ms, trans, event); 556 | break; 557 | case SCSI_WRITE_6: 558 | scsi_write_6(ms, trans, event); 559 | break; 560 | case SCSI_WRITE_10: 561 | scsi_write_10(ms, trans, event); 562 | break; 563 | default: 564 | set_sbc_status(ms, SBC_SENSE_KEY_ILLEGAL_REQUEST, 565 | SBC_ASC_INVALID_COMMAND_OPERATION_CODE, 566 | SBC_ASCQ_NA); 567 | 568 | trans->bytes_to_write = 0; 569 | trans->bytes_to_read = 0; 570 | trans->csw.csw.bCSWStatus = CSW_STATUS_FAILED; 571 | break; 572 | } 573 | } 574 | 575 | /*-- USB Mass Storage Layer --------------------------------------------------*/ 576 | 577 | /** @brief Handle the USB 'OUT' requests. */ 578 | static void msc_data_rx_cb(usbd_device *usbd_dev, uint8_t ep) 579 | { 580 | usbd_mass_storage *ms; 581 | struct usb_msc_trans *trans; 582 | int len, max_len, left; 583 | void *p; 584 | 585 | ms = &_mass_storage; 586 | trans = &ms->trans; 587 | 588 | /* RX only */ 589 | left = sizeof(struct usb_msc_cbw) - trans->cbw_cnt; 590 | if (0 < left) { 591 | max_len = MIN(ms->ep_out_size, left); 592 | p = &trans->cbw.buf[0x1ff & trans->cbw_cnt]; 593 | len = usbd_ep_read_packet(usbd_dev, ep, p, max_len); 594 | trans->cbw_cnt += len; 595 | 596 | if (sizeof(struct usb_msc_cbw) == trans->cbw_cnt) { 597 | scsi_command(ms, trans, EVENT_CBW_VALID); 598 | if (trans->byte_count < trans->bytes_to_read) { 599 | /* We must wait until there is something to 600 | * read again. */ 601 | return; 602 | } 603 | } 604 | } 605 | 606 | if (trans->byte_count < trans->bytes_to_read) { 607 | if (0 < trans->block_count) { 608 | if ((0 == trans->byte_count) && (NULL != ms->lock)) { 609 | (*ms->lock)(); 610 | } 611 | } 612 | 613 | left = trans->bytes_to_read - trans->byte_count; 614 | max_len = MIN(ms->ep_out_size, left); 615 | p = &trans->msd_buf[0x1ff & trans->byte_count]; 616 | len = usbd_ep_read_packet(usbd_dev, ep, p, max_len); 617 | trans->byte_count += len; 618 | 619 | if (0 < trans->block_count) { 620 | if (0 == (0x1ff & trans->byte_count)) { 621 | uint32_t lba; 622 | 623 | lba = trans->lba_start + trans->current_block; 624 | if (0 != (*ms->write_block)(lba, 625 | trans->msd_buf)) { 626 | /* Error */ 627 | } 628 | trans->current_block++; 629 | } 630 | } 631 | 632 | /* Fix "writes aren't acknowledged" bug on Linux (PR #409) */ 633 | if (false == trans->csw_valid) { 634 | scsi_command(ms, trans, EVENT_NEED_STATUS); 635 | trans->csw_valid = true; 636 | } 637 | left = sizeof(struct usb_msc_csw) - trans->csw_sent; 638 | if (0 < left) { 639 | max_len = MIN(ms->ep_out_size, left); 640 | p = &trans->csw.buf[trans->csw_sent]; 641 | len = usbd_ep_write_packet(usbd_dev, ms->ep_in, p, 642 | max_len); 643 | trans->csw_sent += len; 644 | } 645 | 646 | } else if (trans->byte_count < trans->bytes_to_write) { 647 | if (0 < trans->block_count) { 648 | if ((0 == trans->byte_count) && (NULL != ms->lock)) { 649 | (*ms->lock)(); 650 | } 651 | 652 | if (0 == (0x1ff & trans->byte_count)) { 653 | uint32_t lba; 654 | 655 | lba = trans->lba_start + trans->current_block; 656 | if (0 != (*ms->read_block)(lba, 657 | trans->msd_buf)) { 658 | /* Error */ 659 | } 660 | trans->current_block++; 661 | } 662 | } 663 | 664 | left = trans->bytes_to_write - trans->byte_count; 665 | max_len = MIN(ms->ep_out_size, left); 666 | p = &trans->msd_buf[0x1ff & trans->byte_count]; 667 | len = usbd_ep_write_packet(usbd_dev, ms->ep_in, p, max_len); 668 | trans->byte_count += len; 669 | } else { 670 | if (0 < trans->block_count) { 671 | if (trans->current_block == trans->block_count) { 672 | uint32_t lba; 673 | 674 | lba = trans->lba_start + trans->current_block; 675 | if (0 != (*ms->write_block)(lba, 676 | trans->msd_buf)) { 677 | /* Error */ 678 | } 679 | 680 | trans->current_block = 0; 681 | if (NULL != ms->unlock) { 682 | (*ms->unlock)(); 683 | } 684 | } 685 | } 686 | if (false == trans->csw_valid) { 687 | scsi_command(ms, trans, EVENT_NEED_STATUS); 688 | trans->csw_valid = true; 689 | } 690 | 691 | left = sizeof(struct usb_msc_csw) - trans->csw_sent; 692 | if (0 < left) { 693 | max_len = MIN(ms->ep_out_size, left); 694 | p = &trans->csw.buf[trans->csw_sent]; 695 | len = usbd_ep_write_packet(usbd_dev, ms->ep_in, p, 696 | max_len); 697 | trans->csw_sent += len; 698 | } 699 | } 700 | } 701 | 702 | /** @brief Handle the USB 'IN' requests. */ 703 | static void msc_data_tx_cb(usbd_device *usbd_dev, uint8_t ep) 704 | { 705 | usbd_mass_storage *ms; 706 | struct usb_msc_trans *trans; 707 | int len, max_len, left; 708 | void *p; 709 | 710 | ms = &_mass_storage; 711 | trans = &ms->trans; 712 | 713 | if (trans->byte_count < trans->bytes_to_write) { 714 | if (0 < trans->block_count) { 715 | if (0 == (0x1ff & trans->byte_count)) { 716 | uint32_t lba; 717 | 718 | lba = trans->lba_start + trans->current_block; 719 | if (0 != (*ms->read_block)(lba, 720 | trans->msd_buf)) { 721 | /* Error */ 722 | } 723 | trans->current_block++; 724 | } 725 | } 726 | 727 | left = trans->bytes_to_write - trans->byte_count; 728 | max_len = MIN(ms->ep_out_size, left); 729 | p = &trans->msd_buf[0x1ff & trans->byte_count]; 730 | len = usbd_ep_write_packet(usbd_dev, ep, p, max_len); 731 | trans->byte_count += len; 732 | } else { 733 | if (0 < trans->block_count) { 734 | if (trans->current_block == trans->block_count) { 735 | trans->current_block = 0; 736 | if (NULL != ms->unlock) { 737 | (*ms->unlock)(); 738 | } 739 | } 740 | } 741 | if (false == trans->csw_valid) { 742 | scsi_command(ms, trans, EVENT_NEED_STATUS); 743 | trans->csw_valid = true; 744 | } 745 | 746 | left = sizeof(struct usb_msc_csw) - trans->csw_sent; 747 | if (0 < left) { 748 | max_len = MIN(ms->ep_out_size, left); 749 | p = &trans->csw.buf[trans->csw_sent]; 750 | len = usbd_ep_write_packet(usbd_dev, ep, p, max_len); 751 | trans->csw_sent += len; 752 | } else if (sizeof(struct usb_msc_csw) == trans->csw_sent) { 753 | /* End of transaction */ 754 | trans->lba_start = 0xffffffff; 755 | trans->block_count = 0; 756 | trans->current_block = 0; 757 | trans->cbw_cnt = 0; 758 | trans->bytes_to_read = 0; 759 | trans->bytes_to_write = 0; 760 | trans->byte_count = 0; 761 | trans->csw_sent = 0; 762 | trans->csw_valid = false; 763 | } 764 | } 765 | } 766 | 767 | int msc_started = 0; 768 | 769 | /** @brief Handle various control requests related to the msc storage 770 | * interface. 771 | */ 772 | static int msc_control_request(usbd_device *usbd_dev, 773 | struct usb_setup_data *req, uint8_t **buf, 774 | uint16_t *len, 775 | usbd_control_complete_callback *complete) 776 | { 777 | (void)complete; 778 | (void)usbd_dev; 779 | 780 | switch (req->bRequest) { 781 | case USB_MSC_REQ_BULK_ONLY_RESET: 782 | LOG("MSC RESET"); 783 | /* Do any special reset code here. */ 784 | return USBD_REQ_HANDLED; 785 | case USB_MSC_REQ_GET_MAX_LUN: 786 | LOG("GET MAX LUN"); 787 | msc_started = 1; 788 | /* Return the number of LUNs. We use 0. */ 789 | *buf[0] = 0; 790 | *len = 1; 791 | return USBD_REQ_HANDLED; 792 | } 793 | 794 | return USBD_REQ_NOTSUPP; 795 | } 796 | 797 | /** @brief Setup the endpoints to be bulk & register the callbacks. */ 798 | static void msc_set_config(usbd_device *usbd_dev, uint16_t wValue) 799 | { 800 | usbd_mass_storage *ms = &_mass_storage; 801 | 802 | (void)wValue; 803 | 804 | usbd_ep_setup(usbd_dev, ms->ep_in, USB_ENDPOINT_ATTR_BULK, 805 | ms->ep_in_size, msc_data_tx_cb); 806 | usbd_ep_setup(usbd_dev, ms->ep_out, USB_ENDPOINT_ATTR_BULK, 807 | ms->ep_out_size, msc_data_rx_cb); 808 | 809 | usbd_register_control_callback( 810 | usbd_dev, 811 | USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE, 812 | USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, 813 | msc_control_request); 814 | } 815 | 816 | /** @addtogroup usb_msc */ 817 | /** @{ */ 818 | 819 | /** @brief Initializes the USB Mass Storage subsystem. 820 | 821 | @note Currently you can only have this profile active. 822 | 823 | @param[in] usbd_dev The USB device to associate the Mass Storage with. 824 | @param[in] ep_in The USB 'IN' endpoint. 825 | @param[in] ep_in_size The maximum endpoint size. Valid values: 8, 16, 32 or 64 826 | @param[in] ep_out The USB 'OUT' endpoint. 827 | @param[in] ep_out_size The maximum endpoint size. Valid values: 8, 16, 32 or 64 828 | @param[in] vendor_id The SCSI vendor ID to return. Maximum used length is 8. 829 | @param[in] product_id The SCSI product ID to return. Maximum used length is 16. 830 | @param[in] product_revision_level The SCSI product revision level to return. 831 | Maximum used length is 4. 832 | @param[in] block_count The number of 512-byte blocks available. 833 | @param[in] read_block The function called when the host requests to read a LBA 834 | block. Must _NOT_ be NULL. 835 | @param[in] write_block The function called when the host requests to write a 836 | LBA block. Must _NOT_ be NULL. 837 | 838 | @return Pointer to the usbd_mass_storage struct. 839 | */ 840 | usbd_mass_storage *usb_msc_init(usbd_device *usbd_dev, 841 | uint8_t ep_in, uint8_t ep_in_size, 842 | uint8_t ep_out, uint8_t ep_out_size, 843 | const char *vendor_id, 844 | const char *product_id, 845 | const char *product_revision_level, 846 | const uint32_t block_count, 847 | int (*read_block)(uint32_t lba, 848 | uint8_t *copy_to), 849 | int (*write_block)(uint32_t lba, 850 | const uint8_t *copy_from)) 851 | { 852 | _mass_storage.usbd_dev = usbd_dev; 853 | _mass_storage.ep_in = ep_in; 854 | _mass_storage.ep_in_size = ep_in_size; 855 | _mass_storage.ep_out = ep_out; 856 | _mass_storage.ep_out_size = ep_out_size; 857 | _mass_storage.vendor_id = vendor_id; 858 | _mass_storage.product_id = product_id; 859 | _mass_storage.product_revision_level = product_revision_level; 860 | _mass_storage.block_count = block_count - 1; 861 | _mass_storage.read_block = read_block; 862 | _mass_storage.write_block = write_block; 863 | _mass_storage.lock = NULL; 864 | _mass_storage.unlock = NULL; 865 | 866 | _mass_storage.trans.lba_start = 0xffffffff; 867 | _mass_storage.trans.block_count = 0; 868 | _mass_storage.trans.current_block = 0; 869 | _mass_storage.trans.cbw_cnt = 0; 870 | _mass_storage.trans.bytes_to_read = 0; 871 | _mass_storage.trans.bytes_to_write = 0; 872 | _mass_storage.trans.byte_count = 0; 873 | _mass_storage.trans.csw_valid = false; 874 | _mass_storage.trans.csw_sent = 0; 875 | 876 | set_sbc_status_good(&_mass_storage); 877 | 878 | usbd_register_set_config_callback(usbd_dev, msc_set_config); 879 | 880 | return &_mass_storage; 881 | } 882 | 883 | /** @} */ -------------------------------------------------------------------------------- /src/webusb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Devan Lai 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software 5 | * for any purpose with or without fee is hereby granted, provided 6 | * that the above copyright notice and this permission notice 7 | * appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 13 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 15 | * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | #include "webusb.h" 21 | 22 | #include "usb_conf.h" 23 | #include "config.h" 24 | 25 | const struct webusb_platform_descriptor webusb_platform = { 26 | .bLength = WEBUSB_PLATFORM_DESCRIPTOR_SIZE, 27 | .bDescriptorType = USB_DT_DEVICE_CAPABILITY, 28 | .bDevCapabilityType = USB_DC_PLATFORM, 29 | .bReserved = 0, 30 | .platformCapabilityUUID = WEBUSB_UUID, 31 | .bcdVersion = 0x0100, 32 | .bVendorCode = WEBUSB_VENDOR_CODE, 33 | .iLandingPage = 1 34 | }; 35 | -------------------------------------------------------------------------------- /src/webusb.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Devan Lai 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software 5 | * for any purpose with or without fee is hereby granted, provided 6 | * that the above copyright notice and this permission notice 7 | * appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 13 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 15 | * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef WEBUSB_H_INCLUDED 20 | #define WEBUSB_H_INCLUDED 21 | 22 | #include "webusb_defs.h" 23 | 24 | // Arbitrary 25 | #define WEBUSB_VENDOR_CODE 0x01 26 | 27 | extern const struct webusb_platform_descriptor webusb_platform; 28 | extern void webusb_setup(usbd_device* usbd_dev); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/webusb_defs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Devan Lai 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software 5 | * for any purpose with or without fee is hereby granted, provided 6 | * that the above copyright notice and this permission notice 7 | * appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 13 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 15 | * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef WEBUSB_DEFS_H_INCLUDED 20 | #define WEBUSB_DEFS_H_INCLUDED 21 | 22 | #include 23 | 24 | #define WEBUSB_REQ_GET_URL 0x02 25 | 26 | #define WEBUSB_DT_URL 3 27 | 28 | #define WEBUSB_URL_SCHEME_HTTP 0 29 | #define WEBUSB_URL_SCHEME_HTTPS 1 30 | 31 | struct webusb_platform_descriptor { 32 | uint8_t bLength; 33 | uint8_t bDescriptorType; 34 | uint8_t bDevCapabilityType; 35 | uint8_t bReserved; 36 | uint8_t platformCapabilityUUID[16]; 37 | uint16_t bcdVersion; 38 | uint8_t bVendorCode; 39 | uint8_t iLandingPage; 40 | } __attribute__((packed)); 41 | 42 | #define WEBUSB_PLATFORM_DESCRIPTOR_SIZE sizeof(struct webusb_platform_descriptor) 43 | 44 | #define WEBUSB_UUID {0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47,0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65} 45 | 46 | struct webusb_url_descriptor { 47 | uint8_t bLength; 48 | uint8_t bDescriptorType; 49 | uint8_t bScheme; 50 | char URL[]; 51 | } __attribute__((packed)); 52 | 53 | #define WEBUSB_DT_URL_DESCRIPTOR_SIZE 3 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /src/winusb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Devan Lai 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software 5 | * for any purpose with or without fee is hereby granted, provided 6 | * that the above copyright notice and this permission notice 7 | * appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 13 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 15 | * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | #include "winusb.h" 21 | 22 | #include "usb_conf.h" 23 | 24 | static const struct winusb_compatible_id_descriptor winusb_wcid = { 25 | .dwLength = (WINUSB_COMPATIBLE_ID_HEADER_SIZE + 26 | 1*WINUSB_COMPATIBLE_ID_FUNCTION_SECTION_SIZE), 27 | .bcdVersion = 0x0100, 28 | .wIndex = 0x0004, 29 | .bNumSections = 1, 30 | .reserved = { 0, 0, 0, 0, 0, 0, 0 }, 31 | .functions = { 32 | { 33 | .bInterfaceNumber = 0, 34 | .reserved0 = { 1 }, 35 | .compatibleId = "WINUSB", 36 | .subCompatibleId = "", 37 | .reserved1 = { 0, 0, 0, 0, 0, 0} 38 | }, 39 | } 40 | }; 41 | 42 | static int winusb_control_vendor_request(usbd_device *usbd_dev, 43 | struct usb_setup_data *req, 44 | uint8_t **buf, uint16_t *len, 45 | usbd_control_complete_callback* complete) { 46 | (void)complete; 47 | (void)usbd_dev; 48 | 49 | if (req->bRequest != WINUSB_MS_VENDOR_CODE) { 50 | return USBD_REQ_NEXT_CALLBACK; 51 | } 52 | 53 | int status = USBD_REQ_NOTSUPP; 54 | if (((req->bmRequestType & USB_REQ_TYPE_RECIPIENT) == USB_REQ_TYPE_DEVICE) && 55 | (req->wIndex == WINUSB_REQ_GET_COMPATIBLE_ID_FEATURE_DESCRIPTOR)) { 56 | *buf = (uint8_t*)(&winusb_wcid); 57 | if (*len > winusb_wcid.dwLength) { 58 | *len = winusb_wcid.dwLength; 59 | } 60 | status = USBD_REQ_HANDLED; 61 | } else if (((req->bmRequestType & USB_REQ_TYPE_RECIPIENT) == USB_REQ_TYPE_INTERFACE) && 62 | (req->wIndex == WINUSB_REQ_GET_EXTENDED_PROPERTIES_OS_FEATURE_DESCRIPTOR)) { 63 | status = USBD_REQ_NOTSUPP; 64 | } else { 65 | status = USBD_REQ_NOTSUPP; 66 | } 67 | 68 | return status; 69 | } 70 | 71 | static void winusb_set_config(usbd_device* usbd_dev, uint16_t wValue) { 72 | (void)wValue; 73 | usbd_register_control_callback( 74 | usbd_dev, 75 | USB_REQ_TYPE_VENDOR, 76 | USB_REQ_TYPE_TYPE, 77 | winusb_control_vendor_request); 78 | } 79 | 80 | void winusb_setup(usbd_device* usbd_dev) { 81 | usbd_register_extra_string(usbd_dev, 0xEE, "MSFT100!"); 82 | usbd_register_set_config_callback(usbd_dev, winusb_set_config); 83 | 84 | /* Windows probes the compatible ID before setting the configuration, 85 | so also register the callback now */ 86 | 87 | usbd_register_control_callback( 88 | usbd_dev, 89 | USB_REQ_TYPE_VENDOR, 90 | USB_REQ_TYPE_TYPE, 91 | winusb_control_vendor_request); 92 | } 93 | -------------------------------------------------------------------------------- /src/winusb.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Devan Lai 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software 5 | * for any purpose with or without fee is hereby granted, provided 6 | * that the above copyright notice and this permission notice 7 | * appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 13 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 15 | * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef WINUSB_H_INCLUDED 20 | #define WINUSB_H_INCLUDED 21 | 22 | #include "winusb_defs.h" 23 | 24 | /* Arbitrary, but must be equivalent to the last character in 25 | the special OS descriptor string */ 26 | #define WINUSB_MS_VENDOR_CODE 0x21 27 | 28 | extern void winusb_setup(usbd_device* usbd_dev); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/winusb_defs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Devan Lai 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software 5 | * for any purpose with or without fee is hereby granted, provided 6 | * that the above copyright notice and this permission notice 7 | * appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 13 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 15 | * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef WINUSB_DEFS_H_INCLUDED 20 | #define WINUSB_DEFS_H_INCLUDED 21 | 22 | #include 23 | 24 | /* Microsoft OS 1.0 descriptors */ 25 | 26 | /* Extended Compat ID OS Feature Descriptor Specification */ 27 | #define WINUSB_REQ_GET_COMPATIBLE_ID_FEATURE_DESCRIPTOR 0x04 28 | #define WINUSB_REQ_GET_EXTENDED_PROPERTIES_OS_FEATURE_DESCRIPTOR 0x05 29 | 30 | /* Table 2. Function Section */ 31 | struct winusb_compatible_id_function_section { 32 | uint8_t bInterfaceNumber; 33 | uint8_t reserved0[1]; 34 | const char compatibleId[8]; 35 | const char subCompatibleId[8]; 36 | uint8_t reserved1[6]; 37 | } __attribute__((packed)); 38 | 39 | #define WINUSB_COMPATIBLE_ID_FUNCTION_SECTION_SIZE 24 40 | 41 | /* Table 1. Header Section */ 42 | struct winusb_compatible_id_descriptor { 43 | uint32_t dwLength; 44 | uint16_t bcdVersion; 45 | uint16_t wIndex; 46 | uint8_t bNumSections; 47 | uint8_t reserved[7]; 48 | struct winusb_compatible_id_function_section functions[]; 49 | } __attribute__((packed)); 50 | 51 | #define WINUSB_COMPATIBLE_ID_HEADER_SIZE 16 52 | 53 | /* Microsoft OS 2.0 Descriptors Specification */ 54 | 55 | /* Table 8. Microsoft OS 2.0 descriptor wIndex values */ 56 | #define WINUSB_REQ_MS_OS_20_DESCRIPTOR_INDEX 0x07 57 | #define WINUSB_REQ_MS_OS_20_SET_ALT_ENUMERATION 0x08 58 | 59 | /* Table 9. Microsoft OS 2.0 descriptor wDescriptorType values */ 60 | #define WINUSB_DT_MS_OS_20_SET_HEADER_DESCRIPTOR 0x00 61 | #define WINUSB_DT_MS_OS_20_SUBSET_HEADER_CONFIGURATION 0x01 62 | #define WINUSB_DT_MS_OS_20_SUBSET_HEADER_FUNCTION 0x02 63 | #define WINUSB_DT_MS_OS_20_FEATURE_COMPATIBLE_ID 0x03 64 | #define WINUSB_DT_MS_OS_20_FEATURE_REG_PROPERTY 0x04 65 | #define WINUSB_DT_MS_OS_20_FEATURE_MIN_RESUME_TIME 0x05 66 | #define WINUSB_DT_MS_OS_20_FEATURE_MODEL_ID 0x06 67 | #define WINUSB_DT_MS_OS_20_FEATURE_CCGP_DEVICE 0x07 68 | 69 | /* Table 3. Microsoft OS 2.0 descriptor platform capability UUID */ 70 | #define WINUSB_OS_20_UUID {0xDF, 0x60, 0xDD, 0xD8, 0x89, 0x45, 0xC7, 0x4C, 0x9C, 0xD2, 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F} 71 | 72 | /* Table 5. Descriptor set information structure */ 73 | struct winusb_descriptor_set_information { 74 | uint32_t dwWindowsVersion; 75 | uint16_t wMSOSDescriptorSetTotalLength; 76 | uint8_t bMS_VendorCode; 77 | uint8_t bAltEnumCode; 78 | } __attribute__((packed)); 79 | 80 | /* Table 4. Microsoft OS 2.0 platform capability descriptor header */ 81 | struct winusb_platform_descriptor { 82 | uint8_t bLength; 83 | uint8_t bDescriptorType; 84 | uint8_t bDevCapabilityType; 85 | uint8_t bReserved; 86 | uint8_t platformCapabilityUUID[16]; 87 | struct winusb_descriptor_set_information descriptor_set_information[]; 88 | } __attribute__((packed)); 89 | 90 | /* Table 10. Microsoft OS 2.0 descriptor set header */ 91 | struct winusb_descriptor_set_header { 92 | uint16_t wLength; 93 | uint16_t wDescriptorType; 94 | uint32_t dwWindowsVersion; 95 | uint16_t wTotalLength; 96 | } __attribute__((packed)); 97 | 98 | /* Table 11. Configuration subset header */ 99 | struct winusb_configuration_subset_header { 100 | uint16_t wLength; 101 | uint16_t wDescriptorType; 102 | uint8_t bConfigurationValue; 103 | uint8_t bReserved; 104 | uint16_t wTotalLength; 105 | } __attribute__((packed)); 106 | 107 | /* Table 12. Function subset header */ 108 | struct winusb_function_subset_header { 109 | uint16_t wLength; 110 | uint16_t wDescriptorType; 111 | uint8_t bFirstInterface; 112 | uint8_t bReserved; 113 | uint16_t wSubsetLength; 114 | } __attribute__((packed)); 115 | 116 | /* Table 13. Microsoft OS 2.0 compatible ID descriptor */ 117 | struct winusb_20_compatible_id_feature_descriptor { 118 | uint16_t wLength; 119 | uint16_t wDescriptorType; 120 | const char compatibleId[8]; 121 | const char subCompatibleId[8];; 122 | } __attribute__((packed)); 123 | 124 | /* Table 15. wPropertyDataType values for the Microsoft OS 2.0 registry property descriptor */ 125 | #define WINUSB_PROP_DATA_TYPE_REG_SZ 1 126 | #define WINUSB_PROP_DATA_TYPE_REG_EXPAND_SZ 2 127 | #define WINUSB_PROP_DATA_TYPE_REG_BINARY 3 128 | #define WINUSB_PROP_DATA_TYPE_REG_DWORD_LITTLE_ENDIAN 4 129 | #define WINUSB_PROP_DATA_TYPE_REG_DWORD_BIG_ENDIAN 5 130 | #define WINUSB_PROP_DATA_TYPE_REG_LINK 6 131 | #define WINUSB_PROP_DATA_TYPE_REG_REG_MULTI_SZ 7 132 | 133 | /* Table 16. Microsoft OS 2.0 minimum USB recovery time descriptor */ 134 | struct winusb_minimum_recovery_time_descriptor { 135 | uint16_t wLength; 136 | uint16_t wDescriptorType; 137 | uint8_t bResumeRecoveryTime; 138 | uint8_t bResumeSignalingTime; 139 | } __attribute__((packed)); 140 | 141 | /* Table 16. Microsoft OS 2.0 model ID descriptor */ 142 | struct winusb_model_id_descriptor { 143 | uint16_t wLength; 144 | uint16_t wDescriptorType; 145 | uint8_t modelId[16]; 146 | } __attribute__((packed)); 147 | 148 | /* Table 17. Microsoft OS 2.0 CCGP device descriptor */ 149 | struct winusb_ccgp_device_descriptor { 150 | uint16_t wLength; 151 | uint16_t wDescriptorType; 152 | } __attribute__((packed)); 153 | 154 | #endif 155 | -------------------------------------------------------------------------------- /util/install-toolchain.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eo pipefail 3 | URL=https://developer.arm.com/-/media/Files/downloads/gnu-rm/6-2017q2/gcc-arm-none-eabi-6-2017-q2-update-linux.tar.bz2 4 | TOOLCHAIN=gcc-arm-none-eabi-6-2017-q2-update 5 | TOOLCHAINS=$HOME/toolchains 6 | TOOLCHAIN_MISSING=0 7 | GCC=${TOOLCHAINS}/gcc-arm-embedded/bin/arm-none-eabi-gcc 8 | if [[ ! -d "${TOOLCHAINS}/gcc-arm-embedded" ]]; then 9 | TOOLCHAIN_MISSING=1 10 | fi; 11 | if [[ ! -f ${GCC} ]]; then 12 | TOOLCHAIN_MISSING=1 13 | fi; 14 | 15 | if [ $TOOLCHAIN_MISSING -eq 1 ]; then 16 | echo "Installing $TOOLCHAIN from $URL to ${TOOLCHAINS}" 17 | mkdir -p ${TOOLCHAINS} 18 | wget -qO- $URL | tar xj -C ${TOOLCHAINS} 19 | rm -rf ${TOOLCHAINS}/gcc-arm-embedded 20 | ln -s $TOOLCHAIN ${TOOLCHAINS}/gcc-arm-embedded 21 | fi; 22 | 23 | EXISTING_TOOLCHAIN=`readlink -f "${TOOLCHAINS}/gcc-arm-embedded"` 24 | echo "Current toolchain is $EXISTING_TOOLCHAIN" 25 | 26 | if ! ldd ${GCC} >/dev/null; then 27 | echo "${GCC} does not appear to be executable on this machine" 28 | exit 1 29 | fi; 30 | 31 | TOOLCHAIN_VER=`${GCC} --version | head -n 1` 32 | echo "Installed toolchain version is $TOOLCHAIN_VER" 33 | --------------------------------------------------------------------------------