diff --git a/Bootloader/CMakeLists.txt b/Bootloader/CMakeLists.txt new file mode 100644 index 0000000..b8cb8a0 --- /dev/null +++ b/Bootloader/CMakeLists.txt @@ -0,0 +1,173 @@ +###| CMAKE Kiibohd Controller Bootloader |### +# +# Jacob Alexander 2011-2014 +# Due to this file's usefulness: +# +# Released into the Public Domain +# +# This bootloader is based upon the MCHCK dfu-usb bootloader. +# DO NOT USE with Teensy based microcontrollers. +# +### + + + +### +# Chip Selection +# + +#| You _MUST_ set this to match the microcontroller you are trying to compile for +#| You _MUST_ clean the build directory if you change this value +#| +set( CHIP + "mk20dx128vlf5" # McHCK mk20dx128vlf5 +) + + +### +# Bootloader Configuration +# +set ( BOOTLOADER 1 ) + + + +### +# Compiler Intialization +# +set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/../Lib/CMake ) +include( initialize ) + + + +## +# Source Defines +# +set( SRCS + main.c + dfu.c + dfu.desc.c + flash.c + kinetis.c + usb.c +) + +message( STATUS "Bootloader Source Files:" ) +message( "${SRCS}" ) + +#| Add Lib sources to main list +foreach( SRC_FILE ${COMPILER_SRCS} ) + set( SRCS ${SRCS} ${CMAKE_SOURCE_DIR}/../${SRC_FILE} ) +endforeach() + + + + +### +# Directory Includes +# +include_directories( ${CMAKE_SOURCE_DIR}/../Lib ) + + + +### +# Project Description +# + +#| Project +project( kiibohd_bootloader ) + +#| Target Name (output name) +set( TARGET kiibohd_bootloader ) + +#| General Settings +cmake_minimum_required( VERSION 2.8 ) + + + +### +# Generate Header Files +# +configure_file( _buildvars.h buildvars.h ) +include_directories( ${CMAKE_BINARY_DIR} ) + + + +### +# CMake Module Checking +# +find_package( Git REQUIRED ) +find_package( Ctags ) # Optional + + + +### +# ctag Generation +# +if( CTAGS_EXECUTABLE ) + # Generate the ctags + execute_process( COMMAND ctags ${SRCS} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + ) +endif() + + + +### +# Build Targets +# + +#| Create the .ELF file +set( TARGET_ELF ${TARGET}.elf ) +add_executable( ${TARGET_ELF} ${SRCS} ) + + +#| .ELF Properties +set_target_properties( ${TARGET_ELF} PROPERTIES + LINK_FLAGS ${LINKER_FLAGS} + SUFFIX "" # XXX Force Windows to keep the .exe off +) + + +#| Convert the .ELF into a .bin to load onto the McHCK +set( TARGET_BIN ${TARGET}.bin ) +add_custom_command( TARGET ${TARGET_ELF} POST_BUILD + COMMAND ${CMAKE_OBJCOPY} ${BIN_FLAGS} ${TARGET_ELF} ${TARGET_BIN} + COMMENT "Creating binary file to load: ${TARGET_BIN}" +) + + +#| Generate the Extended .LSS +set( TARGET_LSS ${TARGET}.lss ) +add_custom_command( TARGET ${TARGET_ELF} POST_BUILD + COMMAND ${CMAKE_OBJDUMP} ${LSS_FLAGS} ${TARGET_ELF} > ${TARGET_LSS} + COMMENT "Creating Extended Listing: ${TARGET_LSS}" +) + + +#| Generate the Symbol Table .SYM +set( TARGET_SYM ${TARGET}.sym ) +add_custom_command( TARGET ${TARGET_ELF} POST_BUILD + COMMAND ${CMAKE_NM} -n ${TARGET_ELF} > ${TARGET_SYM} + COMMENT "Creating Symbol Table: ${TARGET_SYM}" +) + + +#| Compiler Selection Record +add_custom_command( TARGET ${TARGET_ELF} POST_BUILD + COMMAND ${CMAKE_SOURCE_DIR}/../Lib/CMake/writer compiler ${COMPILER_FAMILY} +) + + + +### +# Size Information +# + +#| After Changes Size Information +add_custom_target( SizeAfter ALL + COMMAND ${CMAKE_SOURCE_DIR}/../Lib/CMake/sizeCalculator ${CMAKE_SIZE} ram ${TARGET_ELF} ${SIZE_RAM} " SRAM" + COMMAND ${CMAKE_SOURCE_DIR}/../Lib/CMake/sizeCalculator ${CMAKE_SIZE} flash ${TARGET_ELF} ${SIZE_FLASH} "Flash" + DEPENDS ${TARGET_ELF} + COMMENT "Chip usage for ${CHIP}" +) + diff --git a/Bootloader/_buildvars.h b/Bootloader/_buildvars.h new file mode 100644 index 0000000..c9ab483 --- /dev/null +++ b/Bootloader/_buildvars.h @@ -0,0 +1,45 @@ +/* Copyright (C) 2013-2014 by Jacob Alexander + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef __buildvars_h +#define __buildvars_h + +// ----- Includes ----- + + + +// ----- Defines ----- + +// You can change these to give your code its own name. +#define STR_MANUFACTURER u"@MANUFACTURER@" +#define STR_PRODUCT u"Kiibohd DFU Bootloader" +#define STR_SERIAL u"@CHIP@" + + +// Mac OS-X and Linux automatically load the correct drivers. On +// Windows, even though the driver is supplied by Microsoft, an +// INF file is needed to load the driver. These numbers need to +// match the INF file. +#define VENDOR_ID @BOOT_VENDOR_ID@ +#define PRODUCT_ID @BOOT_PRODUCT_ID@ + +#endif + diff --git a/Bootloader/dfu.c b/Bootloader/dfu.c new file mode 100644 index 0000000..dbc29a6 --- /dev/null +++ b/Bootloader/dfu.c @@ -0,0 +1,171 @@ +/* Copyright (c) 2011,2012 Simon Schubert <2@0x2c.org>. + * Modifications by Jacob Alexander 2014 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// ----- Local Includes ----- + +#include "usb.h" +#include "dfu.h" + + + +// ----- Functions ----- + +void dfu_write_done( enum dfu_status err, struct dfu_ctx *ctx ) +{ + ctx->status = err; + if (ctx->status == DFU_STATUS_OK) { + switch (ctx->state) { + case DFU_STATE_dfuDNBUSY: + ctx->state = DFU_STATE_dfuDNLOAD_IDLE; + break; + default: + break; + } + } else { + ctx->state = DFU_STATE_dfuERROR; + } +} + +static void dfu_dnload_complete( void *buf, ssize_t len, void *cbdata ) +{ + struct dfu_ctx *ctx = cbdata; + + if (len > 0) + ctx->state = DFU_STATE_dfuDNBUSY; + else + ctx->state = DFU_STATE_dfuMANIFEST; + ctx->status = ctx->finish_write(buf, ctx->off, len); + ctx->off += len; + ctx->len = len; + + if (ctx->status != DFU_STATUS_async) + dfu_write_done(ctx->status, ctx); + + usb_handle_control_status(ctx->state == DFU_STATE_dfuERROR); +} + +static void dfu_reset_system( void *buf, ssize_t len, void *cbdata ) +{ + SOFTWARE_RESET(); +} + +static int dfu_handle_control( struct usb_ctrl_req_t *req, void *data ) +{ + struct dfu_ctx *ctx = data; + int fail = 1; + + switch ((enum dfu_ctrl_req_code)req->bRequest) { + case USB_CTRL_REQ_DFU_DNLOAD: { + void *buf; + + switch (ctx->state) { + case DFU_STATE_dfuIDLE: + ctx->off = 0; + break; + case DFU_STATE_dfuDNLOAD_IDLE: + break; + default: + goto err; + } + + /** + * XXX we are not allowed to STALL here, and we need to eat all transferred data. + * better not allow setup_write to break the protocol. + */ + ctx->status = ctx->setup_write(ctx->off, req->wLength, &buf); + if (ctx->status != DFU_STATUS_OK) { + ctx->state = DFU_STATE_dfuERROR; + goto err_have_status; + } + + if (req->wLength > 0) + usb_ep0_rx(buf, req->wLength, dfu_dnload_complete, ctx); + else + dfu_dnload_complete(NULL, 0, ctx); + goto out_no_status; + } + case USB_CTRL_REQ_DFU_GETSTATUS: { + struct dfu_status_t st; + + st.bState = ctx->state; + st.bStatus = ctx->status; + st.bwPollTimeout = 1000; /* XXX */ + /** + * If we're in DFU_STATE_dfuMANIFEST, we just finished + * the download, and we're just about to send our last + * status report. Once the report has been sent, go + * and reset the system to put the new firmware into + * effect. + */ + usb_ep0_tx_cp(&st, sizeof(st), req->wLength, NULL, NULL); + if (ctx->state == DFU_STATE_dfuMANIFEST) { + usb_handle_control_status_cb(dfu_reset_system); + goto out_no_status; + } + break; + } + case USB_CTRL_REQ_DFU_CLRSTATUS: + ctx->state = DFU_STATE_dfuIDLE; + ctx->status = DFU_STATUS_OK; + break; + case USB_CTRL_REQ_DFU_GETSTATE: { + uint8_t st = ctx->state; + usb_ep0_tx_cp(&st, sizeof(st), req->wLength, NULL, NULL); + break; + } + case USB_CTRL_REQ_DFU_ABORT: + switch (ctx->state) { + case DFU_STATE_dfuIDLE: + case DFU_STATE_dfuDNLOAD_IDLE: + /* case DFU_STATE_dfuUPLOAD_IDLE: */ + ctx->state = DFU_STATE_dfuIDLE; + break; + default: + goto err; + } + break; + /* case USB_CTRL_REQ_DFU_UPLOAD: */ + default: + return (0); + } + + fail = 0; + goto out; + +err: + ctx->status = DFU_STATUS_errSTALLEDPKT; +err_have_status: + ctx->state = DFU_STATE_dfuERROR; +out: + usb_handle_control_status(fail); +out_no_status: + return (1); +} + +void dfu_init( dfu_setup_write_t setup_write, dfu_finish_write_t finish_write, struct dfu_ctx *ctx ) +{ + ctx->state = DFU_STATE_dfuIDLE; + ctx->setup_write = setup_write; + ctx->finish_write = finish_write; + usb_attach_function(&dfu_function, &ctx->header); +} + +const struct usbd_function dfu_function = { + .control = dfu_handle_control, + .interface_count = USB_FUNCTION_DFU_IFACE_COUNT, +}; + diff --git a/Bootloader/dfu.desc.c b/Bootloader/dfu.desc.c new file mode 100644 index 0000000..5d7d69c --- /dev/null +++ b/Bootloader/dfu.desc.c @@ -0,0 +1,115 @@ +// Originally Generated from MCHCK Toolkit +/* Copyright (c) Jacob Alexander 2014 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// ----- Local Includes ----- + +#include "dfu.desc.h" + + + +// ----- Generated Includes ----- + +#include + + + +// ----- Structs ----- + +static const struct usb_config_1 usb_config_1 = { + .config = { + .bLength = sizeof(struct usb_desc_config_t), + .bDescriptorType = USB_DESC_CONFIG, + .wTotalLength = sizeof(struct usb_config_1), + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .one = 1, + .bMaxPower = 100 + }, + .usb_function_0 = { + .iface = { + .bLength = sizeof(struct usb_desc_iface_t), + .bDescriptorType = USB_DESC_IFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_DEV_CLASS_APP, + .bInterfaceSubClass = USB_DEV_SUBCLASS_APP_DFU, + .bInterfaceProtocol = USB_DEV_PROTO_DFU_DFU, + .iInterface = 0 + }, + + .dfu = { + .bLength = sizeof(struct dfu_desc_functional), + .bDescriptorType = { + .id = 0x1, + .type_type = USB_DESC_TYPE_CLASS + }, + .will_detach = 1, + .manifestation_tolerant = 0, + .can_upload = 0, + .can_download = 1, + .wDetachTimeOut = 0, + .wTransferSize = USB_DFU_TRANSFER_SIZE, + .bcdDFUVersion = { .maj = 1, .min = 1 } + } +}, + +}; + +static const struct usbd_config usbd_config_1 = { + .init = init_usb_bootloader, + .suspend = NULL, + .resume = NULL, + .desc = &usb_config_1.config, + .function = {&dfu_function}, +}; + +static const struct usb_desc_dev_t dfu_device_dev_desc = { + .bLength = sizeof(struct usb_desc_dev_t), + .bDescriptorType = USB_DESC_DEV, + .bcdUSB = { .maj = 2 }, + .bDeviceClass = USB_DEV_CLASS_SEE_IFACE, + .bDeviceSubClass = USB_DEV_SUBCLASS_SEE_IFACE, + .bDeviceProtocol = USB_DEV_PROTO_SEE_IFACE, + .bMaxPacketSize0 = EP0_BUFSIZE, + .idVendor = VENDOR_ID, + .idProduct = PRODUCT_ID, + .bcdDevice = { .raw = 0 }, + .iManufacturer = 1, + .iProduct = 2, + .iSerialNumber = 3, + .bNumConfigurations = 1, +}; + +static const struct usb_desc_string_t * const dfu_device_str_desc[] = { + USB_DESC_STRING_LANG_ENUS, + USB_DESC_STRING(STR_MANUFACTURER), + USB_DESC_STRING(STR_PRODUCT), + USB_DESC_STRING(STR_SERIAL), + NULL +}; + +const struct usbd_device dfu_device = { + .dev_desc = &dfu_device_dev_desc, + .string_descs = dfu_device_str_desc, + .configs = { + &usbd_config_1, + NULL + } +}; + diff --git a/Bootloader/dfu.desc.h b/Bootloader/dfu.desc.h new file mode 100644 index 0000000..d5ebb1a --- /dev/null +++ b/Bootloader/dfu.desc.h @@ -0,0 +1,43 @@ +// Originally Generated from MCHCK Toolkit +/* Copyright (c) Jacob Alexander 2014 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __DFU_DESC_H +#define __DFU_DESC_H + +// ----- Local Includes ----- + +#include "mchck.h" + + + +// ----- Variables ----- + +const struct usbd_device dfu_device; + +usbd_init_fun_t init_usb_bootloader; + + + +// ----- Structs ----- + +struct usb_config_1 { + struct usb_desc_config_t config; + struct dfu_function_desc usb_function_0; +}; + +#endif + diff --git a/Bootloader/dfu.h b/Bootloader/dfu.h new file mode 100644 index 0000000..3dd635b --- /dev/null +++ b/Bootloader/dfu.h @@ -0,0 +1,197 @@ +/* Copyright (c) 2011,2012 Simon Schubert <2@0x2c.org>. + * Modifications by Jacob Alexander 2014 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _USB_DFU_H +#define _USB_DFU_H + +// ----- Compiler Includes ----- + +#include + + + +// ----- Defines ----- + +#define USB_FUNCTION_DFU_IFACE_COUNT 1 + + +#ifndef USB_DFU_TRANSFER_SIZE +#define USB_DFU_TRANSFER_SIZE FLASH_SECTOR_SIZE +#endif + +#define USB_FUNCTION_DESC_DFU_DECL \ + struct dfu_function_desc + +#define USB_FUNCTION_DFU_IFACE_COUNT 1 +#define USB_FUNCTION_DFU_RX_EP_COUNT 0 +#define USB_FUNCTION_DFU_TX_EP_COUNT 0 + + + +// ----- Macros ----- + +#define USB_FUNCTION_DESC_DFU(state...) \ + { \ + .iface = { \ + .bLength = sizeof(struct usb_desc_iface_t), \ + .bDescriptorType = USB_DESC_IFACE, \ + .bInterfaceNumber = USB_FUNCTION_IFACE(0, state), \ + .bAlternateSetting = 0, \ + .bNumEndpoints = 0, \ + .bInterfaceClass = USB_DEV_CLASS_APP, \ + .bInterfaceSubClass = USB_DEV_SUBCLASS_APP_DFU, \ + .bInterfaceProtocol = USB_DEV_PROTO_DFU_DFU, \ + .iInterface = 0, \ + }, \ + .dfu = { \ + .bLength = sizeof(struct dfu_desc_functional), \ + .bDescriptorType = { \ + .id = 0x1, \ + .type_type = USB_DESC_TYPE_CLASS \ + }, \ + .will_detach = 1, \ + .manifestation_tolerant = 0, \ + .can_upload = 0, \ + .can_download = 1, \ + .wDetachTimeOut = 0, \ + .wTransferSize = USB_DFU_TRANSFER_SIZE, \ + .bcdDFUVersion = { .maj = 1, .min = 1 } \ + } \ + } + + + +// ----- Enumerations ----- + +enum dfu_dev_subclass { + USB_DEV_SUBCLASS_APP_DFU = 0x01 +}; + +enum dfu_dev_proto { + USB_DEV_PROTO_DFU_APP = 0x01, + USB_DEV_PROTO_DFU_DFU = 0x02 +}; + +enum dfu_ctrl_req_code { + USB_CTRL_REQ_DFU_DETACH = 0, + USB_CTRL_REQ_DFU_DNLOAD = 1, + USB_CTRL_REQ_DFU_UPLOAD = 2, + USB_CTRL_REQ_DFU_GETSTATUS = 3, + USB_CTRL_REQ_DFU_CLRSTATUS = 4, + USB_CTRL_REQ_DFU_GETSTATE = 5, + USB_CTRL_REQ_DFU_ABORT = 6 +}; + +enum dfu_status { + DFU_STATUS_async = 0xff, + DFU_STATUS_OK = 0x00, + DFU_STATUS_errTARGET = 0x01, + DFU_STATUS_errFILE = 0x02, + DFU_STATUS_errWRITE = 0x03, + DFU_STATUS_errERASE = 0x04, + DFU_STATUS_errCHECK_ERASED = 0x05, + DFU_STATUS_errPROG = 0x06, + DFU_STATUS_errVERIFY = 0x07, + DFU_STATUS_errADDRESS = 0x08, + DFU_STATUS_errNOTDONE = 0x09, + DFU_STATUS_errFIRMWARE = 0x0a, + DFU_STATUS_errVENDOR = 0x0b, + DFU_STATUS_errUSBR = 0x0c, + DFU_STATUS_errPOR = 0x0d, + DFU_STATUS_errUNKNOWN = 0x0e, + DFU_STATUS_errSTALLEDPKT = 0x0f +}; + +enum dfu_state { + DFU_STATE_appIDLE = 0, + DFU_STATE_appDETACH = 1, + DFU_STATE_dfuIDLE = 2, + DFU_STATE_dfuDNLOAD_SYNC = 3, + DFU_STATE_dfuDNBUSY = 4, + DFU_STATE_dfuDNLOAD_IDLE = 5, + DFU_STATE_dfuMANIFEST_SYNC = 6, + DFU_STATE_dfuMANIFEST = 7, + DFU_STATE_dfuMANIFEST_WAIT_RESET = 8, + DFU_STATE_dfuUPLOAD_IDLE = 9, + DFU_STATE_dfuERROR = 10 +}; + + + +// ----- Structs ----- + +struct dfu_status_t { + enum dfu_status bStatus : 8; + uint32_t bwPollTimeout : 24; + enum dfu_state bState : 8; + uint8_t iString; +} __packed; +CTASSERT_SIZE_BYTE(struct dfu_status_t, 6); + + +typedef enum dfu_status (*dfu_setup_write_t)(size_t off, size_t len, void **buf); +typedef enum dfu_status (*dfu_finish_write_t)(void *, size_t off, size_t len); +typedef void (*dfu_detach_t)(void); + +struct dfu_ctx { + struct usbd_function_ctx_header header; + enum dfu_state state; + enum dfu_status status; + dfu_setup_write_t setup_write; + dfu_finish_write_t finish_write; + size_t off; + size_t len; +}; + + +struct dfu_desc_functional { + uint8_t bLength; + struct usb_desc_type_t bDescriptorType; /* = class DFU/0x1 FUNCTIONAL */ + union { + struct { + uint8_t can_download : 1; + uint8_t can_upload : 1; + uint8_t manifestation_tolerant : 1; + uint8_t will_detach : 1; + uint8_t _rsvd0 : 4; + }; + uint8_t bmAttributes; + }; + uint16_t wDetachTimeOut; + uint16_t wTransferSize; + struct usb_bcd_t bcdDFUVersion; +} __packed; +CTASSERT_SIZE_BYTE(struct dfu_desc_functional, 9); + +struct dfu_function_desc { + struct usb_desc_iface_t iface; + struct dfu_desc_functional dfu; +}; + + +extern const struct usbd_function dfu_function; +extern const struct usbd_function dfu_app_function; + + + +// ----- Functions ----- + +void dfu_write_done(enum dfu_status, struct dfu_ctx *ctx); +void dfu_init(dfu_setup_write_t setup_write, dfu_finish_write_t finish_write, struct dfu_ctx *ctx); +void dfu_app_init(dfu_detach_t detachcb); + +#endif diff --git a/Bootloader/flash.c b/Bootloader/flash.c new file mode 100644 index 0000000..9ae6dae --- /dev/null +++ b/Bootloader/flash.c @@ -0,0 +1,93 @@ +/* Copyright (c) 2011,2012 Simon Schubert <2@0x2c.org>. + * Modifications by Jacob Alexander 2014 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// ----- Local Includes ----- + +#include "mchck.h" + + + +// ----- Variables ----- + +uint32_t flash_ALLOW_BRICKABLE_ADDRESSES; + + + +// ----- Functions ----- + +/* This will have to live in SRAM. */ +__attribute__((section(".ramtext.ftfl_submit_cmd"), long_call)) +int ftfl_submit_cmd(void) +{ + FTFL.fstat.raw = ((struct FTFL_FSTAT_t){ + .ccif = 1, + .rdcolerr = 1, + .accerr = 1, + .fpviol = 1 + }).raw; + struct FTFL_FSTAT_t stat; + while (!(stat = FTFL.fstat).ccif) + /* NOTHING */; /* XXX maybe WFI? */ + return (!!stat.mgstat0); +} + +int flash_prepare_flashing(void) +{ + /* switch to FlexRAM */ + if (!FTFL.fcnfg.ramrdy) { + FTFL.fccob.set_flexram.fcmd = FTFL_FCMD_SET_FLEXRAM; + FTFL.fccob.set_flexram.flexram_function = FTFL_FLEXRAM_RAM; + return (ftfl_submit_cmd()); + } + return (0); +} + +int flash_erase_sector(uintptr_t addr) +{ + if (addr < (uintptr_t)&_app_rom && + flash_ALLOW_BRICKABLE_ADDRESSES != 0x00023420) + return (-1); + FTFL.fccob.erase.fcmd = FTFL_FCMD_ERASE_SECTOR; + FTFL.fccob.erase.addr = addr; + return (ftfl_submit_cmd()); +} + +int flash_program_section(uintptr_t addr, size_t num_words) +{ + GPIOA_PSOR |= (1<<19); + FTFL.fccob.program_section.fcmd = FTFL_FCMD_PROGRAM_SECTION; + FTFL.fccob.program_section.addr = addr; + FTFL.fccob.program_section.num_words = num_words; + return (ftfl_submit_cmd()); +} + +int flash_program_sector(uintptr_t addr, size_t len) +{ + return (len != FLASH_SECTOR_SIZE || + (addr & (FLASH_SECTOR_SIZE - 1)) != 0 || + flash_erase_sector(addr) || + flash_program_section(addr, FLASH_SECTOR_SIZE/4)); +} + +void *flash_get_staging_area(uintptr_t addr, size_t len) +{ + if ((addr & (FLASH_SECTOR_SIZE - 1)) != 0 || + len != FLASH_SECTOR_SIZE) + return (NULL); + return (FlexRAM); +} + diff --git a/Bootloader/flash.h b/Bootloader/flash.h new file mode 100644 index 0000000..5903295 --- /dev/null +++ b/Bootloader/flash.h @@ -0,0 +1,38 @@ +/* Copyright (c) 2011,2012 Simon Schubert <2@0x2c.org>. + * Modifications by Jacob Alexander 2014 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __FLASH_H +#define __FLASH_H + +// ----- Defines ----- + +#define FLASH_SECTOR_SIZE 1024 + + + +// ----- Functions ----- + +__attribute__((section(".ramtext.ftfl_submit_cmd"), long_call)) +int ftfl_submit_cmd(void); +int flash_prepare_flashing(void); +int flash_erase_sector(uintptr_t); +int flash_program_section(uintptr_t, size_t); +int flash_program_sector(uintptr_t, size_t); +void *flash_get_staging_area(uintptr_t, size_t); + +#endif + diff --git a/Bootloader/ftfl.h b/Bootloader/ftfl.h new file mode 100644 index 0000000..b35bcca --- /dev/null +++ b/Bootloader/ftfl.h @@ -0,0 +1,246 @@ +/* Copyright (c) 2011,2012 Simon Schubert <2@0x2c.org>. + * Modifications by Jacob Alexander 2014 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __FTFL_H +#define __FTFL_H + +// ----- Local Includes ----- + +#include "mchck-cdefs.h" + + + +// ----- Structs ----- + +struct FTFL_FSTAT_t { + UNION_STRUCT_START(8); + uint8_t mgstat0 : 1; + uint8_t _rsvd0 : 3; + uint8_t fpviol : 1; + uint8_t accerr : 1; + uint8_t rdcolerr : 1; + uint8_t ccif : 1; + UNION_STRUCT_END; +}; +CTASSERT_SIZE_BIT(struct FTFL_FSTAT_t, 8); + +struct FTFL_FCNFG_t { + UNION_STRUCT_START(8); + uint8_t eeerdy : 1; + uint8_t ramrdy : 1; + uint8_t pflsh : 1; + uint8_t _rsvd0 : 1; + uint8_t erssusp : 1; + uint8_t ersareq : 1; + uint8_t rdcollie : 1; + uint8_t ccie : 1; + UNION_STRUCT_END; +}; +CTASSERT_SIZE_BIT(struct FTFL_FCNFG_t, 8); + +struct FTFL_FSEC_t { + UNION_STRUCT_START(8); + enum { + FTFL_FSEC_SEC_UNSECURE = 2, + FTFL_FSEC_SEC_SECURE = 3 + } sec : 2; + enum { + FTFL_FSEC_FSLACC_DENY = 1, + FTFL_FSEC_FSLACC_GRANT = 3 + } fslacc : 2; + enum { + FTFL_FSEC_MEEN_DISABLE = 2, + FTFL_FSEC_MEEN_ENABLE = 3 + } meen : 2; + enum { + FTFL_FSEC_KEYEN_DISABLE = 1, + FTFL_FSEC_KEYEN_ENABLE = 2 + } keyen : 2; + UNION_STRUCT_END; +}; +CTASSERT_SIZE_BIT(struct FTFL_FSEC_t, 8); + +struct FTFL_FOPT_t { + UNION_STRUCT_START(8); + uint8_t lpboot : 1; + uint8_t ezport_dis : 1; + uint8_t nmi_dis : 1; + uint8_t _rsvd0 : 5; + UNION_STRUCT_END; +}; +CTASSERT_SIZE_BIT(struct FTFL_FOPT_t, 8); + +/** + * The FCOOB is a weird register file, because it is double big endian, + * which makes for odd gaps and for some data that is big endian, and for + * some that is little endian. + */ +union FTFL_FCCOB_t { + struct ftfl_generic { + uint32_t addr : 24; + enum FTFL_FCMD { + FTFL_FCMD_READ_1s_BLOCK = 0x00, + FTFL_FCMD_READ_1s_SECTION = 0x01, + FTFL_FCMD_PROGRAM_CHECK = 0x02, + FTFL_FCMD_READ_RESOURCE = 0x03, + FTFL_FCMD_PROGRAM_LONGWORD = 0x06, + FTFL_FCMD_ERASE_BLOCK = 0x08, + FTFL_FCMD_ERASE_SECTOR = 0x09, + FTFL_FCMD_PROGRAM_SECTION = 0x0b, + FTFL_FCMD_READ_1s_ALL_BLOCKS = 0x40, + FTFL_FCMD_READ_ONCE = 0x41, + FTFL_FCMD_PROGRAM_ONCE = 0x43, + FTFL_FCMD_ERASE_ALL_BLOCKS = 0x44, + FTFL_FCMD_VERIFY_KEY = 0x45, + FTFL_FCMD_PROGRAM_PARTITION = 0x80, + FTFL_FCMD_SET_FLEXRAM = 0x81 + } fcmd : 8; + uint8_t data_be[8]; + } generic; + struct { + uint32_t addr : 24; + enum FTFL_FCMD fcmd : 8; + uint8_t _rsvd0[3]; + enum FTFL_MARGIN_CHOICE { + FTFL_MARGIN_NORMAL = 0x00, + FTFL_MARGIN_USER = 0x01, + FTFL_MARGIN_FACTORY = 0x02 + } margin : 8; + } read_1s_block; + struct ftfl_data_num_words { + uint32_t addr : 24; + enum FTFL_FCMD fcmd : 8; + uint8_t _rsvd0; + enum FTFL_MARGIN_CHOICE margin : 8; + uint16_t num_words; + } read_1s_section; + struct { + uint32_t addr : 24; + enum FTFL_FCMD fcmd : 8; + uint8_t _rsvd0[3]; + enum FTFL_MARGIN_CHOICE margin : 8; + uint8_t data_be[4]; + } program_check; + struct { + uint32_t addr : 24; + enum FTFL_FCMD fcmd : 8; + uint32_t data; + uint8_t _rsvd0[3]; + enum FTFL_RESOURCE_SELECT { + FTFL_RESOURCE_IFR = 0x00, + FTFL_RESOURCE_VERSION = 0x01 + } resource_select : 8; + } read_resource; + struct { + uint32_t addr : 24; + enum FTFL_FCMD fcmd : 8; + uint8_t data_be[4]; + } program_longword; + struct { + uint32_t addr : 24; + enum FTFL_FCMD fcmd : 8; + } erase; + struct ftfl_data_num_words program_section; + struct { + uint8_t _rsvd0[2]; + enum FTFL_MARGIN_CHOICE margin : 8; + enum FTFL_FCMD fcmd : 8; + } read_1s_all_blocks; + struct ftfl_cmd_once { + uint8_t _rsvd0[2]; + uint8_t idx; + enum FTFL_FCMD fcmd : 8; + uint8_t data_be[4]; + } read_once; + struct ftfl_cmd_once program_once; + struct { + uint8_t _rsvd0[3]; + enum FTFL_FCMD fcmd : 8; + } erase_all; + struct { + uint8_t _rsvd0[3]; + enum FTFL_FCMD fcmd : 8; + uint8_t key_be[8]; + } verify_key; + struct { + uint8_t _rsvd0[3]; + enum FTFL_FCMD fcmd : 8; + uint8_t _rsvd1[2]; + + /* the following enum is analogous to enum + * SIM_FLEXNVM_PARTITION in sim.h, but this one is padded + * with four 1-bits to make an 8-bit value. + */ + + enum FTFL_FLEXNVM_PARTITION { + FTFL_FLEXNVM_DATA_32_EEPROM_0 = 0xF0, + FTFL_FLEXNVM_DATA_24_EEPROM_8 = 0xF1, + FTFL_FLEXNVM_DATA_16_EEPROM_16 = 0xF2, + FTFL_FLEXNVM_DATA_8_EEPROM_24 = 0xF9, + FTFL_FLEXNVM_DATA_0_EEPROM_32 = 0xF3 + } flexnvm_partition : 8; + enum FTFL_EEPROM_SIZE { + FTFL_EEPROM_SIZE_0 = 0x3f, + FTFL_EEPROM_SIZE_32 = 0x39, + FTFL_EEPROM_SIZE_64 = 0x38, + FTFL_EEPROM_SIZE_128 = 0x37, + FTFL_EEPROM_SIZE_256 = 0x36, + FTFL_EEPROM_SIZE_512 = 0x35, + FTFL_EEPROM_SIZE_1024 = 0x34, + FTFL_EEPROM_SIZE_2048 = 0x33 + } eeprom_size : 8; + } program_partition; + struct { + uint8_t _rsvd0[2]; + enum FTFL_FLEXRAM_FUNCTION { + FTFL_FLEXRAM_EEPROM = 0x00, + FTFL_FLEXRAM_RAM = 0xff + } flexram_function : 8; + enum FTFL_FCMD fcmd : 8; + } set_flexram; +}; +CTASSERT_SIZE_BYTE(union FTFL_FCCOB_t, 12); + +struct FTFL_t { + struct FTFL_FSTAT_t fstat; + struct FTFL_FCNFG_t fcnfg; + struct FTFL_FSEC_t fsec; + struct FTFL_FOPT_t fopt; + union FTFL_FCCOB_t fccob; + uint8_t fprot_be[4]; + uint8_t feprot; + uint8_t fdprot; +}; +CTASSERT_SIZE_BYTE(struct FTFL_t, 0x18); + +/* Flash Configuration Field, see Sub-Family Reference Manual, section 28.3.1 */ +struct FTFL_CONFIG_t { + uint8_t key[8]; + uint8_t fprot[4]; + struct FTFL_FSEC_t fsec; + struct FTFL_FOPT_t fopt; + uint8_t feprot; + uint8_t fdprot; +}; +CTASSERT_SIZE_BYTE(struct FTFL_CONFIG_t, 16); + +extern volatile struct FTFL_t FTFL; +extern char FlexRAM[]; +extern struct FTFL_CONFIG_t FTFL_CONFIG; + +#endif + diff --git a/Bootloader/kinetis.c b/Bootloader/kinetis.c new file mode 100644 index 0000000..da6c9f7 --- /dev/null +++ b/Bootloader/kinetis.c @@ -0,0 +1,326 @@ +/* Copyright (c) 2011,2012 Simon Schubert <2@0x2c.org>. + * Modifications by Jacob Alexander 2014 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// ----- Defines ----- + +#define usb_xfer_info USB_STAT_t + + + +// ----- Local Includes ----- + +#include "mchck.h" +#include "usb-internal.h" + + + +// ----- Functions ----- + +/** + * Kinetis USB driver notes: + * We need to manually maintain the DATA0/1 toggling for the SIE. + * SETUP transactions always start with a DATA0. + * + * The SIE internally uses pingpong (double) buffering, which is + * easily confused with DATA0/DATA1 toggling, and I think even the + * Freescale docs confuse the two notions. When BD->DTS is set, + * BD->DATA01 will be used to verify/discard incoming DATAx and it + * will set the DATAx PID for outgoing tokens. This is not described + * as such in the Freescale Kinetis docs, but the Microchip PIC32 OTG + * docs are more clear on this; it seems that both Freescale and + * Microchip use different versions of the same USB OTG IP core. + * + * http://ww1.microchip.com/downloads/en/DeviceDoc/61126F.pdf + * + * Clear CTL->TOKEN_BUSY after SETUP tokens. + */ + + +static struct USB_BD_t bdt[USB_MAX_EP * 2 *2] __attribute__((section(".usbdescriptortable"))); + +static struct USB_BD_t * +usb_get_bd(struct usbd_ep_pipe_state_t *s) +{ + return (&bdt[(s->ep_num << 2) | (s->ep_dir << 1) | s->pingpong]); +} + +static struct USB_BD_t * +usb_get_bd_stat(struct USB_STAT_t *stat) +{ + return (((void *)(uintptr_t)bdt + (stat->raw << 1))); +} + +void *usb_get_xfer_data(struct usb_xfer_info *i) +{ + return (usb_get_bd_stat(i)->addr); +} + +enum usb_tok_pid usb_get_xfer_pid(struct usb_xfer_info *i) +{ + return (usb_get_bd_stat(i)->tok_pid); +} + +int usb_get_xfer_ep(struct usb_xfer_info *i) +{ + return (i->ep); +} + +enum usb_ep_dir usb_get_xfer_dir(struct usb_xfer_info *i) +{ + return (i->dir); +} + +void usb_enable_xfers(void) +{ + USB0.ctl.raw = ((struct USB_CTL_t){ + .txd_suspend = 0, + .usben = 1 + }).raw; +} + +void usb_set_addr(int addr) +{ + USB0.addr.raw = addr; +} + + +void usb_pipe_stall(struct usbd_ep_pipe_state_t *s) +{ + volatile struct USB_BD_t *bd = usb_get_bd(s); + bd->raw = ((struct USB_BD_BITS_t){ + .stall = 1, + .own = 1 + }).raw; +} + +void usb_pipe_unstall(struct usbd_ep_pipe_state_t *s) +{ + volatile struct USB_BD_t *bd = usb_get_bd(s); + struct USB_BD_BITS_t state = { .raw = bd->raw }; + + if (state.own && state.stall) + bd->raw = 0; +} + +void usb_pipe_enable(struct usbd_ep_pipe_state_t *s) +{ + USB0.endpt[s->ep_num].raw |= ((struct USB_ENDPT_t){ + .eptxen = s->ep_dir == USB_EP_TX, + .eprxen = s->ep_dir == USB_EP_RX, + .ephshk = 1, /* XXX ISO */ + .epctldis = s->ep_num != 0 + }).raw; +} + +void usb_pipe_disable(struct usbd_ep_pipe_state_t *s) +{ + USB0.endpt[s->ep_num].raw &= ~((struct USB_ENDPT_t){ + .eptxen = s->ep_dir == USB_EP_TX, + .eprxen = s->ep_dir == USB_EP_RX, + .epctldis = 1 + }).raw; +} + +size_t usb_ep_get_transfer_size(struct usbd_ep_pipe_state_t *s) +{ + struct USB_BD_t *bd = usb_get_bd(s); + return (bd->bc); +} + +void usb_queue_next(struct usbd_ep_pipe_state_t *s, void *addr, size_t len) +{ + volatile struct USB_BD_t *bd = usb_get_bd(s); + + bd->addr = addr; + /* damn you bitfield problems */ + bd->raw = ((struct USB_BD_BITS_t){ + .dts = 1, + .own = 1, + .data01 = s->data01, + .bc = len, + }).raw; +} + +static void usb_reset(void) +{ + /* reset pingpong state */ + /* For some obscure reason, we need to use or here. */ + USB0.ctl.raw |= ((struct USB_CTL_t){ + .txd_suspend = 1, + .oddrst = 1, + }).raw; + + /* clear all interrupt bits - not sure if needed */ + USB0.istat.raw = 0xff; + USB0.errstat.raw = 0xff; + USB0.otgistat.raw = 0xff; + + /* zap also BDT pingpong & queued transactions */ + memset(bdt, 0, sizeof(bdt)); + USB0.addr.raw = 0; + + usb_restart(); + + USB0.ctl.raw = ((struct USB_CTL_t){ + .txd_suspend = 0, + .usben = 1 + }).raw; + + /* we're only interested in reset and transfers */ + USB0.inten.raw = ((struct USB_ISTAT_t){ + .tokdne = 1, + .usbrst = 1, + .stall = 1, + .sleep = 1, + }).raw; + + USB0.usbtrc0.usbresmen = 0; + USB0.usbctrl.susp = 0; +} + +void usb_enable(void) +{ + SIM.sopt2.usbsrc = 1; /* usb from mcg */ + SIM.scgc4.usbotg = 1; /* enable usb clock */ + + /* reset module - not sure if needed */ + USB0.usbtrc0.raw = ((struct USB_USBTRC0_t){ + .usbreset = 1, + .usbresmen = 1 + }).raw; + while (USB0.usbtrc0.usbreset) + /* NOTHING */; + + USB0.bdtpage1 = (uintptr_t)bdt >> 8; + USB0.bdtpage2 = (uintptr_t)bdt >> 16; + USB0.bdtpage3 = (uintptr_t)bdt >> 24; + + USB0.control.raw = ((struct USB_CONTROL_t){ + .dppullupnonotg = 1 /* enable pullup */ + }).raw; + + USB0.usbctrl.raw = 0; /* resume peripheral & disable pulldowns */ + usb_reset(); /* this will start usb processing */ + + /* really only one thing we want */ + USB0.inten.raw = ((struct USB_ISTAT_t){ + .usbrst = 1, + }).raw; + + /** + * Suspend transceiver now - we'll wake up at reset again. + */ + // TODO - Possible removal + USB0.usbctrl.susp = 1; + USB0.usbtrc0.usbresmen = 1; +} + +void USB0_Handler(void) +{ + struct USB_ISTAT_t stat = {.raw = USB0.istat.raw }; + + if (stat.usbrst) { + usb_reset(); + return; + } + if (stat.stall) { + /* XXX need more work for non-0 ep */ + volatile struct USB_BD_t *bd = usb_get_bd(&usb.ep_state[0].rx); + if (bd->stall) + usb_setup_control(); + } + if (stat.tokdne) { + struct usb_xfer_info stat = USB0.stat; + usb_handle_transaction(&stat); + } + if (stat.sleep) { + USB0.inten.sleep = 0; + USB0.inten.resume = 1; + USB0.usbctrl.susp = 1; + USB0.usbtrc0.usbresmen = 1; + + /** + * Clear interrupts now so that we can detect a fresh + * resume later on. + */ + USB0.istat.raw = stat.raw; + + const struct usbd_config *c = usb_get_config_data(-1); + if (c && c->suspend) + c->suspend(); + } + /** + * XXX it is unclear whether we will receive a synchronous + * resume interrupt if we were in sleep. This code assumes we + * do. + */ + if (stat.resume || USB0.usbtrc0.usb_resume_int) { + USB0.inten.resume = 0; + USB0.inten.sleep = 1; + USB0.usbtrc0.usbresmen = 0; + USB0.usbctrl.susp = 0; + + const struct usbd_config *c = usb_get_config_data(-1); + if (c && c->resume) + c->resume(); + + stat.resume = 1; /* always clear bit */ + } + USB0.istat.raw = stat.raw; +} + +void usb_poll(void) +{ + USB0_Handler(); +} + +int usb_tx_serialno(size_t reqlen) +{ + struct usb_desc_string_t *d; + const size_t nregs = 3; + /** + * actually 4, but UIDH is 0xffffffff. Also our output buffer + * is only 64 bytes, and 128 bit + desc header exceeds this by + * 2 bytes. + */ + const size_t len = nregs * 4 * 2 * 2 + sizeof(*d); + + d = usb_ep0_tx_inplace_prepare(len); + + if (d == NULL) + return (-1); + + d->bLength = len; + d->bDescriptorType = USB_DESC_STRING; + + size_t bufpos = 0; + for (size_t reg = 0; reg < nregs; ++reg) { + /* registers run MSW first */ + uint32_t val = (&SIM.uidmh)[reg]; + + for (size_t bits = 32; bits > 0; bits -= 4, val <<= 4) { + int nibble = val >> 28; + + if (nibble > 9) + nibble += 'a' - '9' - 1; + ((char16_t *)d->bString)[bufpos++] = nibble + '0'; + } + } + usb_ep0_tx(d, len, reqlen, NULL, NULL); + return (0); +} + diff --git a/Bootloader/main.c b/Bootloader/main.c new file mode 100644 index 0000000..d1fbb86 --- /dev/null +++ b/Bootloader/main.c @@ -0,0 +1,96 @@ +/* Copyright (c) 2011,2012 Simon Schubert <2@0x2c.org>. + * Modifications by Jacob Alexander 2014 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// ----- Local Includes ----- + +#include "mchck.h" +#include "dfu.desc.h" + + + +// ----- Variables ----- + +/** + * Unfortunately we can't DMA directly to FlexRAM, so we'll have to stage here. + */ +static char staging[FLASH_SECTOR_SIZE]; + + + +// ----- Functions ----- + +static enum dfu_status setup_write(size_t off, size_t len, void **buf) +{ + static int last = 0; + + if (len > sizeof(staging)) + return (DFU_STATUS_errADDRESS); + + // We only allow the last write to be less than one sector size. + if (off == 0) + last = 0; + if (last && len != 0) + return (DFU_STATUS_errADDRESS); + if (len != FLASH_SECTOR_SIZE) { + last = 1; + memset(staging, 0xff, sizeof(staging)); + } + + *buf = staging; + return (DFU_STATUS_OK); +} + +static enum dfu_status finish_write( void *buf, size_t off, size_t len ) +{ + void *target; + if (len == 0) + return (DFU_STATUS_OK); + + target = flash_get_staging_area(off + (uintptr_t)&_app_rom, FLASH_SECTOR_SIZE); + if (!target) + return (DFU_STATUS_errADDRESS); + memcpy(target, buf, len); + if (flash_program_sector(off + (uintptr_t)&_app_rom, FLASH_SECTOR_SIZE) != 0) + return (DFU_STATUS_errADDRESS); + return (DFU_STATUS_OK); +} + + +static struct dfu_ctx dfu_ctx; + +void init_usb_bootloader( int config ) +{ + dfu_init(setup_write, finish_write, &dfu_ctx); +} + +void main() +{ + // Enabling LED to indicate we are in the bootloader + GPIOA_PDDR |= (1<<19); + // Setup pin - A19 - See Lib/pin_map.mchck for more details on pins + PORTA_PCR19 = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); + GPIOA_PSOR |= (1<<19); + + flash_prepare_flashing(); + + usb_init( &dfu_device ); + for (;;) + { + usb_poll(); + } +} + diff --git a/Bootloader/mchck-cdefs.h b/Bootloader/mchck-cdefs.h new file mode 100644 index 0000000..ab96f92 --- /dev/null +++ b/Bootloader/mchck-cdefs.h @@ -0,0 +1,138 @@ +/* Copyright (c) 2011,2012 Simon Schubert <2@0x2c.org>. + * Modifications by Jacob Alexander 2014 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _MCHCK_CDEFS_H +#define _MCHCK_CDEFS_H + +// ----- Compiler Includes ----- + +#include + + + +// ----- Defines & Macros ----- + + +#define _CONCAT(x,y) _CONCAT1(x,y) +#define _CONCAT1(x,y) x ## y +#define _STR(a) #a + +typedef __CHAR16_TYPE__ char16_t; + +#define __packed __attribute__((__packed__)) + +/* From FreeBSD: compile-time asserts */ +#define CTASSERT(x) _Static_assert(x, _STR(x)) + +#define CTASSERT_SIZE_BYTE(t, s) CTASSERT(sizeof(t) == (s)) +#define CTASSERT_SIZE_BIT(t, s) CTASSERT(sizeof(t) * 8 == (s)) + +#define UNION_STRUCT_START(size) \ + union { \ + _CONCAT(_CONCAT(uint, size), _t) raw; \ + struct { \ + /* just to swallow the following semicolon */ \ + struct _CONCAT(_CONCAT(__dummy_, __COUNTER__), _t) {} + +#define UNION_STRUCT_END \ + }; /* struct */ \ + }; /* union */ + + +/** + * From , + * + */ +#define __PP_NARG(...) \ + __PP_NARG_(__0, ## __VA_ARGS__, __PP_RSEQ_N()) +#define __PP_NARG_(...) \ + __PP_ARG_N(__VA_ARGS__) +#define __PP_ARG_N( \ + _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ + _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ + _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ + _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ + _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ + _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ + _61,_62,_63,N,...) N +#define __PP_RSEQ_N() \ + 62,61,60, \ + 59,58,57,56,55,54,53,52,51,50, \ + 49,48,47,46,45,44,43,42,41,40, \ + 39,38,37,36,35,34,33,32,31,30, \ + 29,28,27,26,25,24,23,22,21,20, \ + 19,18,17,16,15,14,13,12,11,10, \ + 9,8,7,6,5,4,3,2,1,0 + +/** + * From + */ +#define __CAT(a, ...) __PRIMITIVE_CAT(a, __VA_ARGS__) +#define __PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__ + +#define __IIF(c) __PRIMITIVE_CAT(__IIF_, c) +#define __IIF_0(t, ...) __VA_ARGS__ +#define __IIF_1(t, ...) t + +#define __COMPL(b) __PRIMITIVE_CAT(__COMPL_, b) +#define __COMPL_0 1 +#define __COMPL_1 0 + +#define __CHECK_N(x, n, ...) n +#define __CHECK(...) __CHECK_N(__VA_ARGS__, 0,) +#define __PROBE(x) x, 1, + +#define __NOT(x) __CHECK(__PRIMITIVE_CAT(__NOT_, x)) +#define __NOT_0 __PROBE(~) + +#define __BOOL(x) __COMPL(__NOT(x)) +#define __IF(c) __IIF(__BOOL(c)) + +#define __EAT(...) +#define __EXPAND(...) __VA_ARGS__ +#define __WHEN(c) __IF(c)(__EXPAND, __EAT) + +#define __HEAD(h, ...) h +#define __TAIL(h, ...) __VA_ARGS__ + +#define __EVAL(...) __EVAL1(__EVAL1(__EVAL1(__VA_ARGS__))) +#define __EVAL1(...) __EVAL2(__EVAL2(__EVAL2(__VA_ARGS__))) +#define __EVAL2(...) __EVAL3(__EVAL3(__EVAL3(__VA_ARGS__))) +#define __EVAL3(...) __EVAL4(__EVAL4(__EVAL4(__VA_ARGS__))) +#define __EVAL4(...) __EVAL5(__EVAL5(__EVAL5(__VA_ARGS__))) +#define __EVAL5(...) __EVAL6(__EVAL6(__EVAL6(__VA_ARGS__))) +#define __EVAL6(...) __VA_ARGS__ + +#define __EMPTY() +#define __DEFER(id) id __EMPTY() +#define __OBSTRUCT(...) __VA_ARGS__ __DEFER(__EMPTY)() +#define __CAT_ARG(f, a) __OBSTRUCT(f) a + +#define __REPEAT(...) __EVAL(__REPEAT_(__VA_ARGS__)) +#define __REPEAT_INNER(...) __OBSTRUCT(__REPEAT_INDIRECT) () (__VA_ARGS__) +#define __REPEAT_INDIRECT() __REPEAT_ +#define __REPEAT_(iter, itermacro, macro, a, ...) \ + __OBSTRUCT(macro)(iter, a) \ + __WHEN(__PP_NARG(__VA_ARGS__)) \ + ( \ + __OBSTRUCT(__REPEAT_INDIRECT) () ( \ + itermacro(iter, a), itermacro, macro, __VA_ARGS__ \ + ) \ + ) + +#endif + diff --git a/Bootloader/mchck.h b/Bootloader/mchck.h new file mode 100644 index 0000000..7771b9e --- /dev/null +++ b/Bootloader/mchck.h @@ -0,0 +1,49 @@ +/* Copyright (c) 2011,2012 Simon Schubert <2@0x2c.org>. + * Modifications by Jacob Alexander 2014 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __MCHCK_H +#define __MCHCK_H + +// ----- Compiler Includes ----- + +#include +#include +#include +#include + + + +// ----- Project Includes ----- + +#include + + + +// ----- Local Includes ----- + +#include "mchck-cdefs.h" + +extern uint32_t _sidata, _sdata, _edata, _sbss, _ebss, _app_rom; + +#include "ftfl.h" +#include "usbotg.h" +#include "sim.h" +#include "flash.h" +#include "usb.h" + +#endif + diff --git a/Bootloader/sim.h b/Bootloader/sim.h new file mode 100644 index 0000000..0c2afa1 --- /dev/null +++ b/Bootloader/sim.h @@ -0,0 +1,332 @@ +/* Copyright (c) 2011,2012 Simon Schubert <2@0x2c.org>. + * Modifications by Jacob Alexander 2014 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __SIM_H +#define __SIM_H + +// ----- Local Includes ----- + +#include "mchck.h" + + + +// ----- Structs ----- + +struct SIM_t { + struct SIM_SOPT1_t { + UNION_STRUCT_START(32); + uint32_t _rsvd0 : 12; + enum { + SIM_RAMSIZE_8KB = 1, + SIM_RAMSIZE_16KB = 3 + } ramsize : 4; + uint32_t _rsvd1 : 2; + enum { + SIM_OSC32KSEL_SYSTEM = 0, + SIM_OSC32KSEL_RTC = 2, + SIM_OSC32KSEL_LPO = 3 + } osc32ksel : 2; + uint32_t _rsvd2 : 9; + uint32_t usbvstby : 1; + uint32_t usbsstby : 1; + uint32_t usbregen : 1; + UNION_STRUCT_END; + } sopt1; + struct SIM_SOPT1CFG_t { + UNION_STRUCT_START(32); + uint32_t _rsvd0 : 24; + uint32_t urwe : 1; + uint32_t uvswe : 1; + uint32_t usswe : 1; + uint32_t _rsvd1 : 5; + UNION_STRUCT_END; + } sopt1cfg; + uint32_t _pad0[(0x1004 - 0x8) / 4]; + struct SIM_SOPT2_t { + UNION_STRUCT_START(32); + uint32_t _rsvd0 : 4; + enum { + SIM_RTCCLKOUTSEL_1HZ = 0, + SIM_RTCCLKOUTSEL_32KHZ = 1 + } rtcclkoutsel : 1; + enum { + SIM_CLKOUTSEL_FLASH = 2, + SIM_CLKOUTSEL_LPO = 3, + SIM_CLKOUTSEL_MCG = 4, + SIM_CLKOUTSEL_RTC = 5, + SIM_CLKOUTSEL_OSC = 6 + } clkoutsel : 3; + uint32_t _rsvd1 : 3; + enum { + SIM_PTD7PAD_SINGLE = 0, + SIM_PTD7PAD_DOUBLE = 1 + } ptd7pad : 1; + enum { + SIM_TRACECLKSEL_MCG = 0, + SIM_TRACECLKSEL_CORE = 1 + } traceclksel : 1; + uint32_t _rsvd2 : 3; + enum { + SIM_PLLFLLSEL_FLL = 0, + SIM_PLLFLLSEL_PLL = 1 + } pllfllsel : 1; + uint32_t _rsvd3 : 1; + enum { + SIM_USBSRC_EXTERNAL = 0, + SIM_USBSRC_MCG = 1 + } usbsrc : 1; + uint32_t _rsvd4 : 13; + UNION_STRUCT_END; + } sopt2; + uint32_t _pad1; + struct SIM_SOPT4_t { + UNION_STRUCT_START(32); + enum sim_ftmflt { + SIM_FTMFLT_FTM = 0, + SIM_FTMFLT_CMP = 1 + } ftm0flt0 : 1; + enum sim_ftmflt ftm0flt1 : 1; + uint32_t _rsvd0 : 2; + enum sim_ftmflt ftm1flt0 : 1; + uint32_t _rsvd1 : 13; + enum { + SIM_FTMCHSRC_FTM = 0, + SIM_FTMCHSRC_CMP0 = 1, + SIM_FTMCHSRC_CMP1 = 2, + SIM_FTMCHSRC_USBSOF = 3 + } ftm1ch0src : 2; + uint32_t _rsvd2 : 4; + enum sim_ftmclksel { + SIM_FTMCLKSEL_CLK0 = 0, + SIM_FTMCLKSEL_CLK1 = 1 + } ftm0clksel : 1; + enum sim_ftmclksel ftm1clksel : 1; + uint32_t _rsvd3 : 2; + enum { + SIM_FTMTRGSRC_HSCMP0 = 0, + SIM_FTMTRGSRC_FTM1 = 1 + } ftm0trg0src : 1; + uint32_t _rsvd4 : 3; + UNION_STRUCT_END; + } sopt4; + struct SIM_SOPT5_t { + UNION_STRUCT_START(32); + enum sim_uarttxsrc { + SIM_UARTTXSRC_UART = 0, + SIM_UARTTXSRC_FTM = 1 + } uart0txsrc : 1; + uint32_t _rsvd0 : 1; + enum sim_uartrxsrc { + SIM_UARTRXSRC_UART = 0, + SIM_UARTRXSRC_CMP0 = 1, + SIM_UARTRXSRC_CMP1 = 2 + } uart0rxsrc : 2; + enum sim_uarttxsrc uart1txsrc : 1; + uint32_t _rsvd1 : 1; + enum sim_uartrxsrc uart1rxsrc : 2; + uint32_t _rsvd2 : 24; + UNION_STRUCT_END; + } sopt5; + uint32_t _pad2; + struct SIM_SOPT7_t { + UNION_STRUCT_START(32); + enum { + SIM_ADCTRGSEL_PDB = 0, + SIM_ADCTRGSEL_HSCMP0 = 1, + SIM_ADCTRGSEL_HSCMP1 = 2, + SIM_ADCTRGSEL_PIT0 = 4, + SIM_ADCTRGSEL_PIT1 = 5, + SIM_ADCTRGSEL_PIT2 = 6, + SIM_ADCTRGSEL_PIT3 = 7, + SIM_ADCTRGSEL_FTM0 = 8, + SIM_ADCTRGSEL_FTM1 = 9, + SIM_ADCTRGSEL_RTCALARM = 12, + SIM_ADCTRGSEL_RTCSECS = 13, + SIM_ADCTRGSEL_LPTIMER = 14 + } adc0trgsel : 4; + enum { + SIM_ADCPRETRGSEL_A = 0, + SIM_ADCPRETRGSEL_B = 1 + } adc0pretrgsel : 1; + uint32_t _rsvd0 : 2; + enum { + SIM_ADCALTTRGEN_PDB = 0, + SIM_ADCALTTRGEN_ALT = 1 + } adc0alttrgen : 1; + uint32_t _rsvd1 : 24; + UNION_STRUCT_END; + } sopt7; + uint32_t _pad3[(0x1024 - 0x101c) / 4]; + struct SIM_SDID_t { + UNION_STRUCT_START(32); + enum { + SIM_PINID_32 = 2, + SIM_PINID_48 = 4, + SIM_PINID_64 = 5 + } pinid : 4; + enum { + SIM_FAMID_K10 = 0, + SIM_FAMID_K20 = 1 + } famid : 3; + uint32_t _rsvd1 : 5; + uint32_t revid : 4; + uint32_t _rsvd2 : 16; + UNION_STRUCT_END; + } sdid; + uint32_t _pad4[(0x1034 - 0x1028) / 4]; + struct SIM_SCGC4_t { + UNION_STRUCT_START(32); + uint32_t _rsvd0 : 1; + uint32_t ewm : 1; + uint32_t cmt : 1; + uint32_t _rsvd1 : 3; + uint32_t i2c0 : 1; + uint32_t _rsvd2 : 3; + uint32_t uart0 : 1; + uint32_t uart1 : 1; + uint32_t uart2 : 1; + uint32_t _rsvd3 : 5; + uint32_t usbotg : 1; + uint32_t cmp : 1; + uint32_t vref : 1; + uint32_t _rsvd4 : 11; + UNION_STRUCT_END; + } scgc4; + struct SIM_SCGC5_t { + UNION_STRUCT_START(32); + uint32_t lptimer : 1; + uint32_t _rsvd0 : 4; + uint32_t tsi : 1; + uint32_t _rsvd1 : 3; + uint32_t porta : 1; + uint32_t portb : 1; + uint32_t portc : 1; + uint32_t portd : 1; + uint32_t porte : 1; + uint32_t _rsvd2 : 18; + UNION_STRUCT_END; + } scgc5; + struct SIM_SCGC6_t { + UNION_STRUCT_START(32); + uint32_t ftfl : 1; + uint32_t dmamux : 1; + uint32_t _rsvd0 : 10; + uint32_t spi0 : 1; + uint32_t _rsvd1 : 2; + uint32_t i2s : 1; + uint32_t _rsvd2 : 2; + uint32_t crc : 1; + uint32_t _rsvd3 : 2; + uint32_t usbdcd : 1; + uint32_t pdb : 1; + uint32_t pit : 1; + uint32_t ftm0 : 1; + uint32_t ftm1 : 1; + uint32_t _rsvd4 : 1; + uint32_t adc0 : 1; + uint32_t _rsvd5 : 1; + uint32_t rtc : 1; + uint32_t _rsvd6 : 2; + UNION_STRUCT_END; + } scgc6; + struct SIM_SCGC7_t { + UNION_STRUCT_START(32); + uint32_t _rsvd0 : 1; + uint32_t dma : 1; + uint32_t _rsvd1 : 30; + UNION_STRUCT_END; + } scgc7; + struct SIM_CLKDIV1_t { + UNION_STRUCT_START(32); + uint32_t _rsvd0 : 16; + uint32_t outdiv4 : 4; + uint32_t _rsvd1 : 4; + uint32_t outdiv2 : 4; + uint32_t outdiv1 : 4; + UNION_STRUCT_END; + } clkdiv1; + struct SIM_CLKDIV2_t { + UNION_STRUCT_START(32); + uint32_t usbfrac : 1; + uint32_t usbdiv : 3; + uint32_t _rsvd0 : 28; + UNION_STRUCT_END; + } clkdiv2; + struct SIM_FCFG1_t { + UNION_STRUCT_START(32); + uint32_t flashdis : 1; + uint32_t flashdoze : 1; + uint32_t _rsvd0 : 6; + + /* the following enum is analogous to enum + * FTFL_FLEXNVM_PARTITION in ftfl.h, but that one is padded + * with four 1-bits to make an 8-bit value. + */ + enum SIM_FLEXNVM_PARTITION { + SIM_FLEXNVM_DATA_32_EEPROM_0 = 0x0, + SIM_FLEXNVM_DATA_24_EEPROM_8 = 0x1, + SIM_FLEXNVM_DATA_16_EEPROM_16 = 0x2, + SIM_FLEXNVM_DATA_8_EEPROM_24 = 0x9, + SIM_FLEXNVM_DATA_0_EEPROM_32 = 0x3 + } depart : 4; + + uint32_t _rsvd1 : 4; + enum { + SIM_EESIZE_2KB = 3, + SIM_EESIZE_1KB = 4, + SIM_EESIZE_512B = 5, + SIM_EESIZE_256B = 6, + SIM_EESIZE_128B = 7, + SIM_EESIZE_64B = 8, + SIM_EESIZE_32B = 9, + SIM_EESIZE_0B = 15 + } eesize : 4; + uint32_t _rsvd2 : 4; + enum { + SIM_PFSIZE_32KB = 3, + SIM_PFSIZE_64KB = 5, + SIM_PFSIZE_128KB = 7 + } pfsize : 4; + enum { + SIM_NVMSIZE_0KB = 0, + SIM_NVMSIZE_32KB = 3 + } nvmsize : 4; + UNION_STRUCT_END; + } fcfg1; + struct SIM_FCFG2_t { + UNION_STRUCT_START(32); + uint32_t _rsvd0 : 16; + uint32_t maxaddr1 : 7; + enum { + SIM_PFLSH_FLEXNVM = 0, + SIM_PFLSH_PROGRAM = 1 + } pflsh : 1; + uint32_t maxaddr0 : 7; + uint32_t _rsvd1 : 1; + UNION_STRUCT_END; + } fcfg2; + uint32_t uidh; + uint32_t uidmh; + uint32_t uidml; + uint32_t uidl; +}; +CTASSERT_SIZE_BYTE(struct SIM_t, 0x1064); + +extern volatile struct SIM_t SIM; + +#endif + diff --git a/Bootloader/usb-common.h b/Bootloader/usb-common.h new file mode 100644 index 0000000..0857017 --- /dev/null +++ b/Bootloader/usb-common.h @@ -0,0 +1,60 @@ +/* Copyright (c) 2011,2012 Simon Schubert <2@0x2c.org>. + * Modifications by Jacob Alexander 2014 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _USB_COMMON_H +#define _USB_COMMON_H + +// ----- Enumerations ----- + +/** + * USB request data structures. + */ + +enum usb_tok_pid { + USB_PID_TIMEOUT = 0, + USB_PID_OUT = 1, + USB_PID_ACK = 2, + USB_PID_DATA0 = 3, + USB_PID_IN = 9, + USB_PID_NAK = 10, + USB_PID_DATA1 = 11, + USB_PID_SETUP = 13, + USB_PID_STALL = 14, + USB_PID_DATAERR = 15 +}; + +enum usb_ep_pingpong { + USB_EP_PINGPONG_EVEN = 0, + USB_EP_PINGPONG_ODD = 1 +}; + +enum usb_ep_dir { + USB_EP_RX = 0, + USB_EP_TX = 1 +}; + +enum usb_data01 { + USB_DATA01_DATA0 = 0, + USB_DATA01_DATA1 = 1 +}; + +enum { + EP0_BUFSIZE = 64 +}; + +#endif + diff --git a/Bootloader/usb-internal.h b/Bootloader/usb-internal.h new file mode 100644 index 0000000..6d9210c --- /dev/null +++ b/Bootloader/usb-internal.h @@ -0,0 +1,104 @@ +/* Copyright (c) 2011,2012 Simon Schubert <2@0x2c.org>. + * Modifications by Jacob Alexander 2014 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __USB_INTERNAL_H +#define __USB_INTERNAL_H + +/** + * Internal driver structures + */ + +/** + * USB state machine + * ================= + * + * Device configuration states: + * + * Attached <-> Powered + * Powered -(reset)-> Default + * Default -(SET_ADDRESS)-> Address + * Address -(SET_CONFIGURATION)-> Configured + * Configured -(SET_CONFIGURATION 0)-> Address + * Address -(SET_ADDRESS 0)-> Default + * [Default, Configured, Address] -(reset)-> Default + */ + +// ----- Defines ----- + +#ifndef USB_MAX_EP +#define USB_MAX_EP 16 +#endif + + + +// ----- Structs ----- + +struct usbd_ep_pipe_state_t { + enum usb_ep_pingpong pingpong; /* next desc to use */ + enum usb_data01 data01; + size_t transfer_size; + size_t pos; + uint8_t *data_buf; + const uint8_t *copy_source; + int short_transfer; + ep_callback_t callback; + void *callback_data; + size_t ep_maxsize; + /* constant */ + int ep_num; + enum usb_ep_dir ep_dir; +}; + +struct usbd_ep_state_t { + union { + struct usbd_ep_pipe_state_t pipe[2]; + struct { + struct usbd_ep_pipe_state_t rx; + struct usbd_ep_pipe_state_t tx; + }; + }; +}; + +struct usbd_t { + struct usbd_function_ctx_header functions; + struct usbd_function control_function; + const struct usbd_device *identity; + int address; + int config; + enum usbd_dev_state { + USBD_STATE_DISABLED = 0, + USBD_STATE_DEFAULT, + USBD_STATE_SETTING_ADDRESS, + USBD_STATE_ADDRESS, + USBD_STATE_CONFIGURED + } state; + enum usb_ctrl_req_dir ctrl_dir; + struct usbd_ep_state_t ep_state[USB_MAX_EP]; +}; + +extern struct usbd_t usb; + + + +// ----- Functions ----- + +void usb_restart(void); +void usb_enable(void); +const struct usbd_config *usb_get_config_data(int config); + +#endif + diff --git a/Bootloader/usb.c b/Bootloader/usb.c new file mode 100644 index 0000000..c93dfc5 --- /dev/null +++ b/Bootloader/usb.c @@ -0,0 +1,590 @@ +/* Copyright (c) 2011,2012 Simon Schubert <2@0x2c.org>. + * Modifications by Jacob Alexander 2014 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// ----- Compiler Includes ----- + +#include +#include +#include + + +// ----- Local Includes ----- + +#include "usb.h" +#include "usb-internal.h" + + + +// ----- Variables ----- + +static uint8_t ep0_buf[2][EP0_BUFSIZE] __attribute__((aligned(4))); +struct usbd_t usb; + + + +// ----- Functions ----- + +/** + * Returns: 0 when this is was the last transfer, 1 if there is still + * more to go. + */ +/* Defaults to EP0 for now */ +static int usb_tx_next(struct usbd_ep_pipe_state_t *s) +{ + + /** + * Us being here means the previous transfer just completed + * successfully. That means the host just toggled its data + * sync bit, and so do we. + */ + s->data01 ^= 1; + + if (s->transfer_size > 0) { + size_t thislen = s->transfer_size; + + if (thislen > s->ep_maxsize) + thislen = s->ep_maxsize; + + void *addr = s->data_buf + s->pos; + + if (s->copy_source) { + /* Bounce buffer mode */ + addr = s->data_buf; + memcpy(addr, s->copy_source + s->pos, thislen); + } + s->pos += thislen; + s->transfer_size -= thislen; + + usb_queue_next(s, addr, thislen); + s->pingpong ^= 1; + + return (1); + } + + /** + * All data has been shipped. Do we need to send a short + * packet? + */ + if (s->short_transfer) { + s->short_transfer = 0; + usb_queue_next(s, NULL, 0); + s->pingpong ^= 1; + return (1); + } + + if (s->callback) + s->callback(s->data_buf, s->pos, s->callback_data); + + return (0); +} + +static void setup_tx(struct usbd_ep_pipe_state_t *s, const void *buf, size_t len, size_t reqlen, ep_callback_t cb, void *cb_data) +{ + s->data_buf = (void *)buf; + s->copy_source = NULL; + s->transfer_size = len; + s->pos = 0; + s->callback = cb; + s->callback_data = cb_data; + if (s->transfer_size > reqlen) + s->transfer_size = reqlen; + if (s->transfer_size < reqlen && s->transfer_size % s->ep_maxsize == 0) + s->short_transfer = 1; + else + s->short_transfer = 0; +} + +static void submit_tx(struct usbd_ep_pipe_state_t *s) +{ + /* usb_tx_next() flips the data toggle, so invert this here. */ + s->data01 ^= 1; + usb_tx_next(s); +} + +/** + * send USB data (IN device transaction) + * + * So far this function is specialized for EP 0 only. + * + * Returns: size to be transfered, or -1 on error. + */ +int usb_tx(struct usbd_ep_pipe_state_t *s, const void *buf, size_t len, size_t reqlen, ep_callback_t cb, void *cb_data) +{ + setup_tx(s, buf, len, reqlen, cb, cb_data); + submit_tx(s); + return (s->transfer_size); +} + + +/** + * Returns: 0 when this is was the last transfer, 1 if there is still + * more to go. + */ +/* Defaults to EP0 for now */ +/* XXX pass usb_stat to validate pingpong */ +static int usb_rx_next(struct usbd_ep_pipe_state_t *s) +{ + /** + * Us being here means the previous transfer just completed + * successfully. That means the host just toggled its data + * sync bit, and so do we. + */ + s->data01 ^= 1; + + size_t thislen = usb_ep_get_transfer_size(s); + + s->transfer_size -= thislen; + s->pos += thislen; + + /** + * We're done with this buffer now. Switch the pingpong now + * before we might have to receive the next piece of data. + */ + s->pingpong ^= 1; + + /** + * If this is a short transfer, or we received what we + * expected, we're done. + */ + if (thislen < s->ep_maxsize || s->transfer_size == 0) { + if (s->callback) + s->callback(s->data_buf, s->pos, s->callback_data); + return (0); + } + + /** + * Otherwise we still need to receive more data. + */ + size_t nextlen = s->transfer_size; + + if (nextlen > s->ep_maxsize) + nextlen = s->ep_maxsize; + + void *addr = s->data_buf + s->pos; + usb_queue_next(s, addr, nextlen); + + return (1); +} + +/** + * Receive USB data (OUT device transaction) + * + * Returns: size to be received, or -1 on error. + */ +int usb_rx(struct usbd_ep_pipe_state_t *s, void *buf, size_t len, ep_callback_t cb, void *cb_data) +{ + s->data_buf = buf; + s->transfer_size = len; + s->pos = 0; + s->callback = cb; + s->callback_data = cb_data; + + size_t thislen = s->transfer_size; + if (thislen > s->ep_maxsize) + thislen = s->ep_maxsize; + + usb_queue_next(s, s->data_buf, thislen); + return (len); +} + +int usb_ep0_tx_cp(const void *buf, size_t len, size_t reqlen, ep_callback_t cb, void *cb_data) +{ + struct usbd_ep_pipe_state_t *s = &usb.ep_state[0].tx; + enum usb_ep_pingpong pp = s->pingpong; + + setup_tx(s, ep0_buf[pp], len, reqlen, cb, cb_data); + s->copy_source = buf; + submit_tx(s); + return (s->transfer_size); +} + +void *usb_ep0_tx_inplace_prepare(size_t len) +{ + enum usb_ep_pingpong pp = usb.ep_state[0].tx.pingpong; + + if (len > EP0_BUFSIZE) + return (NULL); + + return (ep0_buf[pp]); +} + +int usb_ep0_tx(void *buf, size_t len, size_t reqlen, ep_callback_t cb, void *cb_data) +{ + return (usb_tx(&usb.ep_state[0].tx, buf, len, reqlen, cb, cb_data)); +} + +int usb_ep0_rx(void *buf, size_t len, ep_callback_t cb, void *cb_data) +{ + return (usb_rx(&usb.ep_state[0].rx, buf, len, cb, cb_data)); +} + + +const struct usbd_config * +usb_get_config_data(int config) +{ + if (config <= 0) + config = usb.config; + + if (config != 0) + return (usb.identity->configs[config - 1]); + else + return (NULL); +} + +static int usb_set_config(int config) +{ + const struct usbd_config *config_data; + + if (usb.config != 0) { + config_data = usb_get_config_data(-1); + if (config_data != NULL && config_data->init != NULL) + config_data->init(0); + } + + if (config != 0) { + /* XXX overflow */ + config_data = usb_get_config_data(config); + if (config_data != NULL && config_data->init != NULL) + config_data->init(1); + } + usb.config = config; + return (0); +} + +static int usb_set_interface(int iface, int altsetting) +{ + int iface_count = 0; + + for (struct usbd_function_ctx_header *fh = &usb.functions; + fh != NULL; + fh = fh->next, iface_count += fh->function->interface_count) { + if (iface - iface_count < fh->function->interface_count) { + if (fh->function->configure != NULL) + return (fh->function->configure(iface, + iface - iface_count, + altsetting, + fh)); + + /* Default to a single altsetting */ + if (altsetting != 0) + return (-1); + else + return (0); + } + } + + return (-1); +} + +static int usb_tx_config_desc(int idx, int reqlen) +{ + const struct usb_desc_config_t *d = usb.identity->configs[idx]->desc; + + usb_ep0_tx_cp(d, d->wTotalLength, reqlen, NULL, NULL); + return (0); +} + +static int usb_tx_string_desc(int idx, int reqlen) +{ + const struct usb_desc_string_t * const *d; + + for (d = usb.identity->string_descs; idx != 0 && *d != NULL; ++d) + --idx; + switch ((uintptr_t)*d) { + case (uintptr_t)NULL: + return (-1); + case (uintptr_t)USB_DESC_STRING_SERIALNO: + return (usb_tx_serialno(reqlen)); + default: + usb_ep0_tx_cp(*d, (*d)->bLength, reqlen, NULL, NULL); + return (0); + } +} + + +static void usb_handle_control_done(void *data, ssize_t len, void *cbdata) +{ + if (usb.state == USBD_STATE_SETTING_ADDRESS) { + usb.state = USBD_STATE_ADDRESS; + usb_set_addr(usb.address); + } + usb_setup_control(); +} + +void usb_handle_control_status_cb(ep_callback_t cb) +{ + /* empty status transfer */ + switch (usb.ctrl_dir) { + case USB_CTRL_REQ_IN: + usb.ep_state[0].rx.data01 = USB_DATA01_DATA1; + usb_rx(&usb.ep_state[0].rx, NULL, 0, cb, NULL); + break; + + default: + usb.ep_state[0].tx.data01 = USB_DATA01_DATA1; + usb_ep0_tx_cp(NULL, 0, 1 /* short packet */, cb, NULL); + break; + } +} + +void usb_handle_control_status(int fail) +{ + if (fail) { + usb_pipe_stall(&usb.ep_state[0].rx); + usb_pipe_stall(&usb.ep_state[0].tx); + } else { + usb_handle_control_status_cb(usb_handle_control_done); + } +} + + +/** + * Dispatch non-standard request to registered USB functions. + */ +static void usb_handle_control_nonstd(struct usb_ctrl_req_t *req) +{ + /* XXX filter by interface/endpoint? */ + for (struct usbd_function_ctx_header *fh = &usb.functions; fh != NULL; fh = fh->next) { + /* ->control() returns != 0 if it handled the request */ + if (fh->function->control != NULL && + fh->function->control(req, fh)) + return; + } + + usb_handle_control_status(-1); +} + + +/** + * + * Great resource: http://wiki.osdev.org/Universal_Serial_Bus + * + * Control Transfers + * ----------------- + * + * A control transfer consists of a SETUP transaction (1), zero or + * more data transactions (IN or OUT) (2), and a final status + * transaction (3). + * + * Token sequence (data toggle): + * 1. SETUP (0) + * (2a. OUT (1) ... (toggling)) + * 3a. IN (1) + * + * or + * 1. SETUP (0) + * 2b. IN (1) ... (toggling) + * 3b. OUT (1) + * + * Report errors by STALLing the control EP after (1) or (2), so that + * (3) will STALL. Seems we need to clear the STALL after that so + * that the next SETUP can make it through. + * + * + */ + +/** + * The following code is not written defensively, but instead only + * asserts values that are essential for correct execution. It + * accepts a superset of the protocol defined by the standard. We do + * this to save space. + */ + +static void usb_handle_control(void *data, ssize_t len, void *cbdata) +{ + struct usb_ctrl_req_t *req = data; + uint16_t zero16 = 0; + int fail = 1; + + usb.ctrl_dir = req->in; + + if (req->type != USB_CTRL_REQ_STD) { + usb_handle_control_nonstd(req); + return; + } + + /* Only STD requests here */ + switch (req->bRequest) { + case USB_CTRL_REQ_GET_STATUS: + /** + * Because we don't support remote wakeup or + * self-powered operation, and we are specialized to + * only EP 0 so far, all GET_STATUS replies are just + * empty. + */ + usb_ep0_tx_cp(&zero16, sizeof(zero16), req->wLength, NULL, NULL); + break; + + case USB_CTRL_REQ_CLEAR_FEATURE: + case USB_CTRL_REQ_SET_FEATURE: + /** + * Nothing to do. Maybe return STALLs on illegal + * accesses? + */ + break; + + case USB_CTRL_REQ_SET_ADDRESS: + /** + * We must keep our previous address until the end of + * the status stage; therefore we can't set the + * address right now. Since this is a special case, + * the EP 0 handler will take care of this later on. + */ + usb.address = req->wValue & 0x7f; + usb.state = USBD_STATE_SETTING_ADDRESS; + break; + + case USB_CTRL_REQ_GET_DESCRIPTOR: + switch (req->wValue >> 8) { + case USB_DESC_DEV: + usb_ep0_tx_cp(usb.identity->dev_desc, usb.identity->dev_desc->bLength, + req->wLength, NULL, NULL); + fail = 0; + break; + case USB_DESC_CONFIG: + fail = usb_tx_config_desc(req->wValue & 0xff, req->wLength); + break; + case USB_DESC_STRING: + fail = usb_tx_string_desc(req->wValue & 0xff, req->wLength); + break; + default: + fail = -1; + break; + } + /* we set fail already, so we can go directly to `err' */ + goto err; + + case USB_CTRL_REQ_GET_CONFIGURATION: + usb_ep0_tx_cp(&usb.config, 1, req->wLength, NULL, NULL); /* XXX implicit LE */ + break; + + case USB_CTRL_REQ_SET_CONFIGURATION: + if (usb_set_config(req->wValue) < 0) + goto err; + break; + + case USB_CTRL_REQ_GET_INTERFACE: + /* We only support iface setting 0 */ + usb_ep0_tx_cp(&zero16, 1, req->wLength, NULL, NULL); + break; + + case USB_CTRL_REQ_SET_INTERFACE: + if (usb_set_interface(req->wIndex, req->wValue) < 0) + goto err; + break; + + default: + goto err; + } + + fail = 0; + +err: + usb_handle_control_status(fail); +} + +void usb_setup_control(void) +{ + void *buf = ep0_buf[usb.ep_state[0].rx.pingpong]; + + usb.ep_state[0].rx.data01 = USB_DATA01_DATA0; + usb.ep_state[0].tx.data01 = USB_DATA01_DATA1; + usb_rx(&usb.ep_state[0].rx, buf, EP0_BUFSIZE, usb_handle_control, NULL); +} + + +/** + * This is called by the interrupt handler + */ +void usb_handle_transaction(struct usb_xfer_info *info) +{ + enum usb_tok_pid pid = usb_get_xfer_pid(info); + struct usbd_ep_state_t *eps = &usb.ep_state[usb_get_xfer_ep(info)]; + struct usbd_ep_pipe_state_t *s = &eps->pipe[usb_get_xfer_dir(info)]; + + switch (pid) { + case USB_PID_SETUP: + case USB_PID_OUT: + /** + * If we receive a SETUP transaction, but don't expect + * it (callback set to somewhere else), stall the EP. + */ + if (pid == USB_PID_SETUP && s->callback != usb_handle_control) + usb_handle_control_status(1); + else + usb_rx_next(s); + if (pid == USB_PID_SETUP) + usb_enable_xfers(); + break; + case USB_PID_IN: + usb_tx_next(s); + break; + default: + break; + } +} + +struct usbd_ep_pipe_state_t *usb_init_ep(struct usbd_function_ctx_header *ctx, int ep, enum usb_ep_dir dir, size_t size) +{ + struct usbd_ep_pipe_state_t *s; + + if (dir == USB_EP_RX) + s = &usb.ep_state[ctx->ep_rx_offset + ep].rx; + else + s = &usb.ep_state[ctx->ep_tx_offset + ep].tx; + + memset(s, 0, sizeof(*s)); + s->ep_maxsize = size; + s->ep_num = ep; + s->ep_dir = dir; + usb_pipe_enable(s); + return (s); +} + +void usb_restart(void) +{ + const struct usbd_device *identity = usb.identity; + /* XXX reset existing functions? */ + memset(&usb, 0, sizeof(usb)); + usb.functions.function = &usb.control_function; + usb.identity = identity; + usb_init_ep(&usb.functions, 0, USB_EP_RX, EP0_BUFSIZE); + usb_init_ep(&usb.functions, 0, USB_EP_TX, EP0_BUFSIZE); + usb_setup_control(); +} + +void usb_attach_function(const struct usbd_function *function, struct usbd_function_ctx_header *ctx) +{ + /* XXX right now this requires a sequential initialization */ + struct usbd_function_ctx_header *prev = &usb.functions; + + while (prev->next != NULL) + prev = prev->next; + ctx->next = NULL; + ctx->function = function; + ctx->interface_offset = prev->interface_offset + prev->function->interface_count; + ctx->ep_rx_offset = prev->ep_rx_offset + prev->function->ep_rx_count; + ctx->ep_tx_offset = prev->ep_tx_offset + prev->function->ep_tx_count; + prev->next = ctx; +} + +void usb_init(const struct usbd_device *identity) +{ + usb.identity = identity; + usb_enable(); +} + diff --git a/Bootloader/usb.h b/Bootloader/usb.h new file mode 100644 index 0000000..cb7b638 --- /dev/null +++ b/Bootloader/usb.h @@ -0,0 +1,513 @@ +/* Copyright (c) 2011,2012 Simon Schubert <2@0x2c.org>. + * Modifications by Jacob Alexander 2014 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __USB_H +#define __USB_H + +// ----- Compiler Includes ----- + +#include + + + +// ----- Local Includes ----- + +#include "mchck.h" +#include "usb-common.h" + + + +// ----- Defines ----- + +#define USB_CTRL_REQ_DIR_SHIFT 0 +#define USB_CTRL_REQ_TYPE_SHIFT 1 +#define USB_CTRL_REQ_RECP_SHIFT 3 +#define USB_CTRL_REQ_CODE_SHIFT 8 +#define USB_CTRL_REQ(req_inout, req_type, req_code) \ + (uint16_t) \ + ((USB_CTRL_REQ_##req_inout << USB_CTRL_REQ_DIR_SHIFT) \ + |(USB_CTRL_REQ_##req_type << USB_CTRL_REQ_TYPE_SHIFT) \ + |(USB_CTRL_REQ_##req_code << USB_CTRL_REQ_CODE_SHIFT)) + + + +// ----- Macros ----- + +#define USB_DESC_STRING(s) \ + (const void *)&(const struct { \ + struct usb_desc_string_t dsc; \ + char16_t str[sizeof(s) / 2 - 1]; \ + }) {{ \ + .bLength = sizeof(struct usb_desc_string_t) + \ + sizeof(s) - 2, \ + .bDescriptorType = USB_DESC_STRING, \ + }, \ + s \ + } +#define USB_DESC_STRING_LANG_ENUS USB_DESC_STRING(u"\x0409") +#define USB_DESC_STRING_SERIALNO ((const void *)1) + +#define USB_FUNCTION_IFACE(iface, iface_off, tx_ep_off, rx_ep_off) \ + ((iface_off) + (iface)) +#define USB_FUNCTION_TX_EP(ep, iface_off, tx_ep_off, rx_ep_off) \ + ((tx_ep_off) + (ep)) +#define USB_FUNCTION_RX_EP(ep, iface_off, tx_ep_off, rx_ep_off) \ + ((rx_ep_off) + (ep)) + + +#define USB__INCREMENT(i, _0) (i + 1) +#define USB__COUNT_IFACE_EP(i, e) \ + __DEFER(USB__COUNT_IFACE_EP_)(__EXPAND i, e) +#define USB__COUNT_IFACE_EP_(iface, tx_ep, rx_ep, func) \ + (iface + USB_FUNCTION_ ## func ## _IFACE_COUNT, \ + tx_ep + USB_FUNCTION_ ## func ## _TX_EP_COUNT, \ + rx_ep + USB_FUNCTION_ ## func ## _RX_EP_COUNT) +#define USB__GET_FUNCTION_IFACE_COUNT(iter, func) \ + USB_FUNCTION_ ## func ## _IFACE_COUNT + + +#define USB__DEFINE_FUNCTION_DESC(iter, func) \ + USB_FUNCTION_DESC_ ## func ## _DECL __CAT(__usb_func_desc, __COUNTER__); +#define USB__INIT_FUNCTION_DESC(iter, func) \ + USB_FUNCTION_DESC_ ## func iter, + +#define USB__DEFINE_CONFIG_DESC(confignum, name, ...) \ + &((const struct name { \ + struct usb_desc_config_t config; \ + __REPEAT_INNER(, __EAT, USB__DEFINE_FUNCTION_DESC, __VA_ARGS__) \ + }){ \ + .config = { \ + .bLength = sizeof(struct usb_desc_config_t), \ + .bDescriptorType = USB_DESC_CONFIG, \ + .wTotalLength = sizeof(struct name), \ + .bNumInterfaces = __REPEAT_INNER(, __EAT, USB__GET_FUNCTION_IFACE_COUNT, __VA_ARGS__) 0, \ + .bConfigurationValue = confignum, \ + .iConfiguration = 0, \ + .one = 1, \ + .bMaxPower = 50 \ + }, \ + __REPEAT_INNER((0, 0, 0), USB__COUNT_IFACE_EP, USB__INIT_FUNCTION_DESC, __VA_ARGS__) \ + }).config + + +#define USB__DEFINE_CONFIG(iter, args) \ + __DEFER(USB__DEFINE_CONFIG_)(iter, __EXPAND args) + +#define USB__DEFINE_CONFIG_(confignum, initfun, ...) \ + &(const struct usbd_config){ \ + .init = initfun, \ + .desc = USB__DEFINE_CONFIG_DESC( \ + confignum, \ + __CAT(__usb_desc, __COUNTER__), \ + __VA_ARGS__) \ + }, + +#define USB_INIT_DEVICE(vid, pid, manuf, product, ...) \ + { \ + .dev_desc = &(const struct usb_desc_dev_t){ \ + .bLength = sizeof(struct usb_desc_dev_t), \ + .bDescriptorType = USB_DESC_DEV, \ + .bcdUSB = { .maj = 2 }, \ + .bDeviceClass = USB_DEV_CLASS_SEE_IFACE, \ + .bDeviceSubClass = USB_DEV_SUBCLASS_SEE_IFACE, \ + .bDeviceProtocol = USB_DEV_PROTO_SEE_IFACE, \ + .bMaxPacketSize0 = EP0_BUFSIZE, \ + .idVendor = vid, \ + .idProduct = pid, \ + .bcdDevice = { .raw = 0 }, \ + .iManufacturer = 1, \ + .iProduct = 2, \ + .iSerialNumber = 3, \ + .bNumConfigurations = __PP_NARG(__VA_ARGS__), \ + }, \ + .string_descs = (const struct usb_desc_string_t * const []){ \ + USB_DESC_STRING_LANG_ENUS, \ + USB_DESC_STRING(manuf), \ + USB_DESC_STRING(product), \ + USB_DESC_STRING_SERIALNO, \ + NULL \ + }, \ + .configs = { \ + __REPEAT(1, USB__INCREMENT, USB__DEFINE_CONFIG, __VA_ARGS__) \ + NULL \ + } \ + } + + + +// ----- Structs & Enumerations ----- + +/** + * Note: bitfields ahead. + * GCC fills the fields lsb-to-msb on little endian. + */ + +/** + * USB descriptors + */ + +enum usb_desc_type { + USB_DESC_DEV = 1, + USB_DESC_CONFIG = 2, + USB_DESC_STRING = 3, + USB_DESC_IFACE = 4, + USB_DESC_EP = 5, + USB_DESC_DEVQUAL = 6, + USB_DESC_OTHERSPEED = 7, + USB_DESC_POWER = 8 +}; + +struct usb_desc_type_t { + UNION_STRUCT_START(8); + enum usb_desc_type id : 5; + enum usb_desc_type_type { + USB_DESC_TYPE_STD = 0, + USB_DESC_TYPE_CLASS = 1, + USB_DESC_TYPE_VENDOR = 2 + } type_type : 2; + uint8_t _rsvd0 : 1; + UNION_STRUCT_END; +}; +CTASSERT_SIZE_BYTE(struct usb_desc_type_t, 1); + +enum usb_dev_class { + USB_DEV_CLASS_SEE_IFACE = 0, + USB_DEV_CLASS_APP = 0xfe, + USB_DEV_CLASS_VENDOR = 0xff +}; + +enum usb_dev_subclass { + USB_DEV_SUBCLASS_SEE_IFACE = 0, + USB_DEV_SUBCLASS_VENDOR = 0xff +}; + +enum usb_dev_proto { + USB_DEV_PROTO_SEE_IFACE = 0, + USB_DEV_PROTO_VENDOR = 0xff +}; + +struct usb_bcd_t { + UNION_STRUCT_START(16); + struct { + uint8_t sub : 4; + uint8_t min : 4; + uint16_t maj : 8; + }; + UNION_STRUCT_END; +}; +CTASSERT_SIZE_BYTE(struct usb_bcd_t, 2); + +struct usb_desc_generic_t { + uint8_t bLength; + struct usb_desc_type_t bDescriptorType; + uint8_t data[]; +}; +CTASSERT_SIZE_BYTE(struct usb_desc_generic_t, 2); + +struct usb_desc_dev_t { + uint8_t bLength; + enum usb_desc_type bDescriptorType : 8; /* = USB_DESC_DEV */ + struct usb_bcd_t bcdUSB; /* = 0x0200 */ + enum usb_dev_class bDeviceClass : 8; + enum usb_dev_subclass bDeviceSubClass : 8; + enum usb_dev_proto bDeviceProtocol : 8; + uint8_t bMaxPacketSize0; + uint16_t idVendor; + uint16_t idProduct; + struct usb_bcd_t bcdDevice; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; + uint8_t bNumConfigurations; +}; +CTASSERT_SIZE_BYTE(struct usb_desc_dev_t, 18); + +struct usb_desc_ep_t { + uint8_t bLength; + enum usb_desc_type bDescriptorType : 8; /* = USB_DESC_EP */ + union { + struct { + uint8_t ep_num : 4; + uint8_t _rsvd0 : 3; + uint8_t in : 1; + }; + uint8_t bEndpointAddress; + }; + struct { + enum usb_ep_type { + USB_EP_CONTROL = 0, + USB_EP_ISO = 1, + USB_EP_BULK = 2, + USB_EP_INTR = 3 + } type : 2; + enum usb_ep_iso_synctype { + USB_EP_ISO_NOSYNC = 0, + USB_EP_ISO_ASYNC = 1, + USB_EP_ISO_ADAPTIVE = 2, + USB_EP_ISO_SYNC = 3 + } sync_type : 2; + enum usb_ep_iso_usagetype { + USB_EP_ISO_DATA = 0, + USB_EP_ISO_FEEDBACK = 1, + USB_EP_ISO_IMPLICIT = 2 + } usage_type : 2; + uint8_t _rsvd1 : 2; + }; + struct { + uint16_t wMaxPacketSize : 11; + uint16_t _rsvd2 : 5; + }; + uint8_t bInterval; +} __packed; +CTASSERT_SIZE_BYTE(struct usb_desc_ep_t, 7); + +struct usb_desc_iface_t { + uint8_t bLength; + enum usb_desc_type bDescriptorType : 8; /* = USB_DESC_IFACE */ + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + enum usb_dev_class bInterfaceClass : 8; + enum usb_dev_subclass bInterfaceSubClass: 8; + enum usb_dev_proto bInterfaceProtocol : 8; + uint8_t iInterface; +}; +CTASSERT_SIZE_BYTE(struct usb_desc_iface_t, 9); + +struct usb_desc_config_t { + uint8_t bLength; + enum usb_desc_type bDescriptorType : 8; /* = USB_DESC_CONFIG */ + uint16_t wTotalLength; /* size of config, iface, ep */ + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; + uint8_t iConfiguration; + struct { + uint8_t _rsvd0 : 5; + uint8_t remote_wakeup : 1; + uint8_t self_powered : 1; + uint8_t one : 1; /* = 1 for historical reasons */ + }; + uint8_t bMaxPower; /* units of 2mA */ +} __packed; +CTASSERT_SIZE_BYTE(struct usb_desc_config_t, 9); + +struct usb_desc_string_t { + uint8_t bLength; + enum usb_desc_type bDescriptorType : 8; /* = USB_DESC_STRING */ + const char16_t bString[]; +}; +CTASSERT_SIZE_BYTE(struct usb_desc_string_t, 2); + +struct usb_ctrl_req_t { + union /* reqtype and request & u16 */ { + struct /* reqtype and request */ { + union /* reqtype in bitfield & u8 */ { + struct /* reqtype */ { + enum usb_ctrl_req_recp { + USB_CTRL_REQ_DEV = 0, + USB_CTRL_REQ_IFACE = 1, + USB_CTRL_REQ_EP = 2, + USB_CTRL_REQ_OTHER = 3 + } recp : 5; + enum usb_ctrl_req_type { + USB_CTRL_REQ_STD = 0, + USB_CTRL_REQ_CLASS = 1, + USB_CTRL_REQ_VENDOR = 2 + } type : 2; + enum usb_ctrl_req_dir { + USB_CTRL_REQ_OUT = 0, + USB_CTRL_REQ_IN = 1 + } in : 1; + }; + uint8_t bmRequestType; + }; /* union */ + enum usb_ctrl_req_code { + USB_CTRL_REQ_GET_STATUS = 0, + USB_CTRL_REQ_CLEAR_FEATURE = 1, + USB_CTRL_REQ_SET_FEATURE = 3, + USB_CTRL_REQ_SET_ADDRESS = 5, + USB_CTRL_REQ_GET_DESCRIPTOR = 6, + USB_CTRL_REQ_SET_DESCRIPTOR = 7, + USB_CTRL_REQ_GET_CONFIGURATION = 8, + USB_CTRL_REQ_SET_CONFIGURATION = 9, + USB_CTRL_REQ_GET_INTERFACE = 10, + USB_CTRL_REQ_SET_INTERFACE = 11, + USB_CTRL_REQ_SYNC_FRAME = 12 + } bRequest : 8; + }; /* struct */ + uint16_t type_and_req; + }; /* union */ + union { + uint16_t wValue; + struct { + uint8_t wValueLow; + uint8_t wValueHigh; + }; + }; + uint16_t wIndex; + uint16_t wLength; +}; +CTASSERT_SIZE_BYTE(struct usb_ctrl_req_t, 8); + +/** + * status replies for GET_STATUS. + */ + +struct usb_ctrl_req_status_dev_t { + uint16_t self_powered : 1; + uint16_t remote_wakeup : 1; + uint16_t _rsvd : 14; +}; +CTASSERT_SIZE_BIT(struct usb_ctrl_req_status_dev_t, 16); + +struct usb_ctrl_req_status_iface_t { + uint16_t _rsvd; +}; +CTASSERT_SIZE_BIT(struct usb_ctrl_req_status_iface_t, 16); + +struct usb_ctrl_req_status_ep_t { + uint16_t halt : 1; + uint16_t _rsvd : 15; +}; +CTASSERT_SIZE_BIT(struct usb_ctrl_req_status_ep_t, 16); + +/** + * Descriptor type (in req->value) for GET_DESCRIPTOR. + */ +struct usb_ctrl_req_desc_t { + uint8_t idx; + enum usb_desc_type type : 8; +}; +CTASSERT_SIZE_BIT(struct usb_ctrl_req_desc_t, 16); + +/** + * Feature selector (in req->value) for CLEAR_FEATURE. + */ +enum usb_ctrl_req_feature { + USB_CTRL_REQ_FEAT_EP_HALT = 0, + USB_CTRL_REQ_FEAT_DEV_REMOTE_WKUP = 1, + USB_CTRL_REQ_FEAT_TEST_MODE = 2 +}; + + +struct usb_xfer_info; +typedef void (*ep_callback_t)(void *buf, ssize_t len, void *data); + +/** + * (Artificial) function. Aggregates one or more interfaces. + */ +struct usbd_function { + int (*configure)(int orig_iface, int iface, int altsetting, void *data); + int (*control)(struct usb_ctrl_req_t *, void *); + int interface_count; + int ep_rx_count; + int ep_tx_count; +}; + +struct usbd_function_ctx_header { + struct usbd_function_ctx_header *next; + const struct usbd_function *function; + int interface_offset; + int ep_rx_offset; + int ep_tx_offset; +}; + + +typedef void (usbd_init_fun_t)(int); +typedef void (usbd_suspend_resume_fun_t)(void); + +/** + * Configuration. Contains one or more functions which all will be + * active concurrently. + */ +struct usbd_config { + usbd_init_fun_t *init; + usbd_suspend_resume_fun_t *suspend; + usbd_suspend_resume_fun_t *resume; + /** + * We will not set a config for now, because there is not much to + * configure, except for power + * + * const struct usb_desc_config_t *config_desc; + */ + const struct usb_desc_config_t *desc; + const struct usbd_function *function[]; +}; + + +/** + * Device. Contains one or more configurations, out of which only one + * is active at a time. + */ +struct usbd_device { + const struct usb_desc_dev_t *dev_desc; + const struct usb_desc_string_t * const *string_descs; + const struct usbd_config *configs[]; +}; + + + +/* Provided by MD code */ +struct usbd_ep_pipe_state_t; + + + +// ----- Functions ----- + +void *usb_get_xfer_data(struct usb_xfer_info *); +enum usb_tok_pid usb_get_xfer_pid(struct usb_xfer_info *); +int usb_get_xfer_ep(struct usb_xfer_info *); +enum usb_ep_dir usb_get_xfer_dir(struct usb_xfer_info *); +void usb_enable_xfers(void); +void usb_set_addr(int); +void usb_ep_stall(int); +size_t usb_ep_get_transfer_size(struct usbd_ep_pipe_state_t *); +void usb_queue_next(struct usbd_ep_pipe_state_t *, void *, size_t); +void usb_pipe_stall(struct usbd_ep_pipe_state_t *); +void usb_pipe_unstall(struct usbd_ep_pipe_state_t *); +void usb_pipe_enable(struct usbd_ep_pipe_state_t *s); +void usb_pipe_disable(struct usbd_ep_pipe_state_t *s); +#ifdef VUSB +void vusb_main_loop(void); +#else +void usb_poll(void); +#endif +int usb_tx_serialno(size_t reqlen); + +/* Provided by MI code */ +void usb_init(const struct usbd_device *); +void usb_attach_function(const struct usbd_function *function, struct usbd_function_ctx_header *ctx); +void usb_handle_transaction(struct usb_xfer_info *); +void usb_setup_control(void); +void usb_handle_control_status_cb(ep_callback_t cb); +void usb_handle_control_status(int); +struct usbd_ep_pipe_state_t *usb_init_ep(struct usbd_function_ctx_header *ctx, int ep, enum usb_ep_dir dir, size_t size); +int usb_rx(struct usbd_ep_pipe_state_t *, void *, size_t, ep_callback_t, void *); +int usb_tx(struct usbd_ep_pipe_state_t *, const void *, size_t, size_t, ep_callback_t, void *); + +int usb_ep0_rx(void *, size_t, ep_callback_t, void *); +void *usb_ep0_tx_inplace_prepare(size_t len); +int usb_ep0_tx(void *buf, size_t len, size_t reqlen, ep_callback_t cb, void *cb_data); +int usb_ep0_tx_cp(const void *, size_t, size_t, ep_callback_t, void *); + + + +// ----- DFU USB Additional Includes ----- + +#include "dfu.h" + +#endif + diff --git a/Bootloader/usbotg.h b/Bootloader/usbotg.h new file mode 100644 index 0000000..f1793f2 --- /dev/null +++ b/Bootloader/usbotg.h @@ -0,0 +1,301 @@ +/* Copyright (c) 2011,2012 Simon Schubert <2@0x2c.org>. + * Modifications by Jacob Alexander 2014 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// ----- Local Includes ----- + +#include "mchck.h" +#include "usb-common.h" + + + +// ----- Structs ----- + +/** + * Hardware structures + */ + +struct USB_ADDINFO_t { + UNION_STRUCT_START(8); + uint8_t iehost : 1; + uint8_t _rsvd0 : 2; + uint8_t irqnum : 5; + UNION_STRUCT_END; +}; +CTASSERT_SIZE_BIT(struct USB_ADDINFO_t, 8); + +struct USB_OTGSTAT_t { + UNION_STRUCT_START(8); + uint8_t avbus : 1; + uint8_t _rsvd0 : 1; + uint8_t b_sess : 1; + uint8_t sessvld : 1; + uint8_t _rsvd1 : 1; + uint8_t line_state : 1; + uint8_t onemsec : 1; + uint8_t idchg : 1; + UNION_STRUCT_END; +}; +CTASSERT_SIZE_BIT(struct USB_OTGSTAT_t, 8); + +struct USB_OTGCTL_t { + UNION_STRUCT_START(8); + uint8_t _rsvd0 : 2; + uint8_t otgen : 1; + uint8_t _rsvd1 : 1; + uint8_t dmlow : 1; + uint8_t dplow : 1; + uint8_t _rsvd2 : 1; + uint8_t dphigh : 1; + UNION_STRUCT_END; +}; +CTASSERT_SIZE_BIT(struct USB_OTGCTL_t, 8); + +struct USB_ISTAT_t { + UNION_STRUCT_START(8); + uint8_t usbrst : 1; + uint8_t error : 1; + uint8_t softok : 1; + uint8_t tokdne : 1; + uint8_t sleep : 1; + uint8_t resume : 1; + uint8_t attach : 1; + uint8_t stall : 1; + UNION_STRUCT_END; +}; +CTASSERT_SIZE_BIT(struct USB_ISTAT_t, 8); + +struct USB_ERRSTAT_t { + UNION_STRUCT_START(8); + uint8_t piderr : 1; + uint8_t crc5eof : 1; + uint8_t crc16 : 1; + uint8_t dfn8 : 1; + uint8_t btoerr : 1; + uint8_t dmaerr : 1; + uint8_t _rsvd0 : 1; + uint8_t btserr : 1; + UNION_STRUCT_END; +}; +CTASSERT_SIZE_BIT(struct USB_ERRSTAT_t, 8); + +struct USB_STAT_t { + UNION_STRUCT_START(8); + uint8_t _rsvd0 : 2; + enum usb_ep_pingpong pingpong : 1; + enum usb_ep_dir dir : 1; + uint8_t ep : 4; + UNION_STRUCT_END; +}; +CTASSERT_SIZE_BIT(struct USB_STAT_t, 8); + +struct USB_CTL_t { + union { + struct /* common */ { + uint8_t _rsvd1 : 1; + uint8_t oddrst : 1; + uint8_t resume : 1; + uint8_t _rsvd2 : 3; + uint8_t se0 : 1; + uint8_t jstate : 1; + }; + struct /* host */ { + uint8_t sofen : 1; + uint8_t _rsvd3 : 2; + uint8_t hostmodeen : 1; + uint8_t reset : 1; + uint8_t token_busy : 1; + uint8_t _rsvd4 : 2; + }; + struct /* device */ { + uint8_t usben : 1; + uint8_t _rsvd5 : 4; + uint8_t txd_suspend : 1; + uint8_t _rsvd6 : 2; + }; + uint8_t raw; + }; +}; +CTASSERT_SIZE_BIT(struct USB_CTL_t, 8); + +struct USB_ADDR_t { + UNION_STRUCT_START(8); + uint8_t addr : 7; + uint8_t lsen : 1; + UNION_STRUCT_END; +}; +CTASSERT_SIZE_BIT(struct USB_ADDR_t, 8); + +struct USB_TOKEN_t { + UNION_STRUCT_START(8); + uint8_t endpt : 4; + enum usb_tok_pid pid : 4; + UNION_STRUCT_END; +}; +CTASSERT_SIZE_BIT(struct USB_TOKEN_t, 8); + +struct USB_ENDPT_t { + UNION_STRUCT_START(8); + uint8_t ephshk : 1; + uint8_t epstall : 1; + uint8_t eptxen : 1; + uint8_t eprxen : 1; + uint8_t epctldis : 1; + uint8_t _rsvd0 : 1; + uint8_t retrydis : 1; + uint8_t hostwohub : 1; + UNION_STRUCT_END; +}; +CTASSERT_SIZE_BIT(struct USB_ENDPT_t, 8); + +struct USB_USBCTRL_t { + UNION_STRUCT_START(8); + uint8_t _rsvd0 : 6; + uint8_t pde : 1; + uint8_t susp : 1; + UNION_STRUCT_END; +}; +CTASSERT_SIZE_BIT(struct USB_USBCTRL_t, 8); + +struct USB_OBSERVE_t { + UNION_STRUCT_START(8); + uint8_t _rsvd0 : 4; + uint8_t dmpd : 1; + uint8_t _rsvd1 : 1; + uint8_t dppd : 1; + uint8_t dppu : 1; + UNION_STRUCT_END; +}; +CTASSERT_SIZE_BIT(struct USB_OBSERVE_t, 8); + +struct USB_CONTROL_t { + UNION_STRUCT_START(8); + uint8_t _rsvd0 : 4; + uint8_t dppullupnonotg : 1; + uint8_t _rsvd1 : 3; + UNION_STRUCT_END; +}; +CTASSERT_SIZE_BIT(struct USB_CONTROL_t, 8); + +struct USB_USBTRC0_t { + UNION_STRUCT_START(8); + uint8_t usb_resume_int : 1; + uint8_t sync_det : 1; + uint8_t _rsvd0 : 3; + uint8_t usbresmen : 1; + uint8_t _rsvd1 : 1; + uint8_t usbreset : 1; + UNION_STRUCT_END; +}; +CTASSERT_SIZE_BIT(struct USB_USBTRC0_t, 8); + +struct USB_t { + uint8_t perid; + uint8_t _pad0[3]; + uint8_t idcomp; + uint8_t _pad1[3]; + uint8_t rev; + uint8_t _pad2[3]; + struct USB_ADDINFO_t addinfo; + uint8_t _pad3[3]; + struct USB_OTGSTAT_t otgistat; + uint8_t _pad4[3]; + struct USB_OTGSTAT_t otgicr; + uint8_t _pad5[3]; + struct USB_OTGSTAT_t otgstat; + uint8_t _pad6[3]; + struct USB_OTGCTL_t otgctl; + uint8_t _pad7[3]; + uint8_t _pad8[0x80 - 0x20]; + struct USB_ISTAT_t istat; + uint8_t _pad9[3]; + struct USB_ISTAT_t inten; + uint8_t _pad10[3]; + struct USB_ERRSTAT_t errstat; + uint8_t _pad11[3]; + struct USB_ERRSTAT_t erren; + uint8_t _pad12[3]; + struct USB_STAT_t stat; + uint8_t _pad13[3]; + struct USB_CTL_t ctl; + uint8_t _pad14[3]; + struct USB_ADDR_t addr; + uint8_t _pad15[3]; + uint8_t bdtpage1; + uint8_t _pad16[3]; + uint8_t frmnuml; + uint8_t _pad17[3]; + struct { + uint8_t frmnumh : 3; + uint8_t _rsvd0 : 5; + }; + uint8_t _pad18[3]; + struct USB_TOKEN_t token; + uint8_t _pad19[3]; + uint8_t softhld; + uint8_t _pad20[3]; + uint8_t bdtpage2; + uint8_t _pad21[3]; + uint8_t bdtpage3; + uint8_t _pad22[3]; + uint8_t _pad23[0xc0 - 0xb8]; + struct { + struct USB_ENDPT_t; + uint8_t _pad24[3]; + } endpt[16]; + struct USB_USBCTRL_t usbctrl; + uint8_t _pad25[3]; + struct USB_OBSERVE_t observe; + uint8_t _pad26[3]; + struct USB_CONTROL_t control; + uint8_t _pad27[3]; + struct USB_USBTRC0_t usbtrc0; + uint8_t _pad28[3]; + uint8_t _pad29[4]; + uint8_t usbfrmadjust; + uint8_t _pad30[3]; +}; +CTASSERT_SIZE_BYTE(struct USB_t, 0x118); + +struct USB_BD_t { + struct USB_BD_BITS_t { + union { + struct { + uint32_t _rsvd0 : 2; + uint32_t stall : 1; + uint32_t dts : 1; + uint32_t ninc : 1; + uint32_t keep : 1; + enum usb_data01 data01 : 1; + uint32_t own : 1; + uint32_t _rsvd1 : 8; + uint32_t bc : 10; + uint32_t _rsvd2 : 6; + }; + struct /* processor */ { + uint32_t _rsvd5 : 2; + enum usb_tok_pid tok_pid : 4; + uint32_t _rsvd6 : 26; + }; + uint32_t raw; + }; + }; + void *addr; +}; +CTASSERT_SIZE_BYTE(struct USB_BD_t, 8); + +extern volatile struct USB_t USB0; + diff --git a/Lib/mk20dx128vlf5.bootloader.ld b/Lib/mk20dx128vlf5.bootloader.ld new file mode 100644 index 0000000..1d45a8e --- /dev/null +++ b/Lib/mk20dx128vlf5.bootloader.ld @@ -0,0 +1,112 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2013 PJRC.COM, LLC. + * Modifications by Jacob Alexander 2014 for use with McHCK + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +MEMORY +{ + FLASH (rx) : ORIGIN = 0x0, LENGTH = 128K + FLASH_APP (rx) : ORIGIN = 4K, LENGTH = 128K-4K + RAM (rwx) : ORIGIN = 0x20000000 - 16K / 2, LENGTH = 16K +} + +/* Starting Address of the application ROM */ +_app_rom = ORIGIN( FLASH_APP ); + +FlexRAM = 0x14000000; +FTFL = 0x40020000; +SCB = 0xe000ed00; +USB0 = 0x40072000; +SIM = 0x40047000; + +/* Section Definitions */ +SECTIONS +{ + .text : + { + . = 0; + KEEP(* (.vectors)) + *(.startup*) + *(.rodata*) + . = 0x400; + KEEP(* (.flashconfig)) + *(.text*) + . = ALIGN(4); + KEEP(*(.init)) + } > FLASH + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } > FLASH + _etext = .; + + .usbdescriptortable (NOLOAD) : { + . = ALIGN(512); + *(.usbdescriptortable*) + } > RAM + + .dmabuffers (NOLOAD) : { + . = ALIGN(4); + *(.dmabuffers*) + } > RAM + + .usbbuffers (NOLOAD) : { + . = ALIGN(4); + *(.usbbuffers*) + } > RAM + + .data : AT (_etext) { + . = ALIGN(4); + _sdata = .; + *(SORT_BY_ALIGNMENT(.ramtext.*) SORT_BY_ALIGNMENT(.data*)) + *(.data*) + . = ALIGN(4); + _edata = .; + } > RAM + + .noinit (NOLOAD) : { + *(.noinit*) + } > RAM + + .bss : { + . = ALIGN(4); + _sbss = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + _ebss = .; + __bss_end = .; + } > RAM + + _estack = ORIGIN(RAM) + LENGTH(RAM); +} +