diff --git a/sw/include/dfu.h b/sw/include/dfu.h new file mode 100644 index 0000000..51e4b51 --- /dev/null +++ b/sw/include/dfu.h @@ -0,0 +1,81 @@ +#ifndef _DFU_H +#define _DFU_H + +/* + * Fadecandy DFU Bootloader + * + * Copyright (c) 2013 Micah Elizabeth Scott + * + * 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. + */ + +#pragma once +#include +#include + +typedef enum { + appIDLE = 0, + appDETACH, + dfuIDLE, + dfuDNLOAD_SYNC, + dfuDNBUSY, + dfuDNLOAD_IDLE, + dfuMANIFEST_SYNC, + dfuMANIFEST, + dfuMANIFEST_WAIT_RESET, + dfuUPLOAD_IDLE, + dfuERROR +} dfu_state_t; + +typedef enum { + OK = 0, + errTARGET, + errFILE, + errWRITE, + errERASE, + errCHECK_ERASED, + errPROG, + errVERIFY, + errADDRESS, + errNOTDONE, + errFIRMWARE, + errVENDOR, + errUSBR, + errPOR, + errUNKNOWN, + errSTALLEDPKT, +} dfu_status_t; + +#define DFU_INTERFACE 0 +#define DFU_DETACH_TIMEOUT 10000 // 10 second timer +#define DFU_TRANSFER_SIZE 1024 // Flash sector size + +// Main thread +void dfu_init(); + +// USB entry points. Always successful. +uint8_t dfu_getstate(); + +// USB entry points. True on success, false for stall. +bool dfu_getstatus(uint8_t status[8]); +bool dfu_clrstatus(); +bool dfu_abort(); +bool dfu_download(unsigned blockNum, unsigned blockLength, +unsigned packetOffset, unsigned packetLength, const uint8_t *data); + +#endif /* _DFU_H */ \ No newline at end of file diff --git a/sw/include/toboot-api.h b/sw/include/toboot-api.h new file mode 100644 index 0000000..a957f79 --- /dev/null +++ b/sw/include/toboot-api.h @@ -0,0 +1,109 @@ +#ifndef TOBOOT_API_H_ +#define TOBOOT_API_H_ + +#include + +/// Store this configuration struct at offset 0x94 from the start +/// of your binary image. +/// You may set all RESERVED values to 0. as they will be calculated +/// when the program is written to flash. +struct toboot_configuration { + /// Set to 0x907070b2 to indicate a valid configuration header. + uint32_t magic; + + /// Reserved value. Used as a generational counter. Toboot will + /// overwrite this value with a monotonically-increasing counter + /// every time a new image is burned. This is used to determine + /// which image is the newest. + uint16_t reserved_gen; + + /// The starting page for your program in 1024-byte blocks. + /// Toboot itself sets this to 0. If this is nonzero, then it + /// must be located after the Toboot image. Toboot is currently + /// under 5 kilobytes, so make sure this value is at least 6. + uint8_t start; + + /// Configuration bitmask. See below for values. + uint8_t config; + + /// Set to 0x18349420 to prevent the user from entering Toboot manually. + /// Use this value with caution, as it can be used to lockout a Tomu. + uint32_t lock_entry; + + /// A bitmask of sectors to erase when updating the program. Each "1" + /// causes that sector to be erased, unless it's Toboot itself. Bit values + /// determine flash blocks 0-31. + uint32_t erase_mask_lo; + + /// A bitmask of sectors to erase when updating the program. Each "1" + /// causes that sector to e erased. Use these two values to e.g. force + /// private keys to be erased when updating. Bit values determine flash + /// blocks 32-63. + uint32_t erase_mask_hi; + + /// A hash of the entire header, minus this entry. Toboot calculates + /// this when it programs the first block. A Toboot configuration + /// header MUST have a valid hash in order to be considered valid. + uint32_t reserved_hash; +} __attribute__((packed)); + +/// Toboot V1.0 leaves IRQs enabled, mimicking the behavior of +/// AN0042. Toboot V2.0 makes this configurable by adding a +/// bit to the configuration area. +#define TOBOOT_CONFIG_FLAG_ENABLE_IRQ_MASK 0x01 +#define TOBOOT_CONFIG_FLAG_ENABLE_IRQ_SHIFT 0 +#define TOBOOT_CONFIG_FLAG_ENABLE_IRQ (1 << 0) +#define TOBOOT_CONFIG_FLAG_DISABLE_IRQ (0 << 0) + +/// When running a normal program, you won't want Toboot to run. +/// However, when developing new software it is handy to have +/// Toboot run at poweron, instead of jumping straight to your +/// program. Set this flag to enter your program whenever the +/// system has just powered on. +#define TOBOOT_CONFIG_FLAG_AUTORUN_MASK 0x02 +#define TOBOOT_CONFIG_FLAG_AUTORUN_SHIFT 1 +#define TOBOOT_CONFIG_FLAG_AUTORUN (1 << 1) +#define TOBOOT_CONFIG_FLAG_AUTORUN_DISABLED (0 << 1) + +/// When we create a fake header, this flag will be set. Otherwise, +/// leave the flag cleared. This field has no meaning in user +/// applications, and is only used internally. +#define TOBOOT_CONFIG_FAKE_MASK 0x04 +#define TOBOOT_CONFIG_FAKE_SHIFT 2 +#define TOBOOT_CONFIG_FAKE (1 << 2) + +/// Various magic values describing Toboot configuration headers. +#define TOBOOT_V1_MAGIC 0x6fb0 +#define TOBOOT_V1_MAGIC_MASK 0x0000ffff +#define TOBOOT_V2_MAGIC 0x907070b2 +#define TOBOOT_V2_MAGIC_MASK 0xffffffff + +/// This value is used to prevent manual entry into Toboot. +#define TOBOOT_LOCKOUT_MAGIC 0x18349420 + +/// The seed value for the hash of Toboot's configuration header +#define TOBOOT_HASH_SEED 0x037a5cf1 + +/// This is the runtime part that lives at the start of RAM. +/// Running programs can use this to force entry into Toboot +/// during the next reboot. +struct toboot_runtime { + /// Set this to 0x74624346 and reboot to enter bootloader, + /// even if LOCKOUT is enabled. + uint32_t magic; + + /// Set this to 0 when your program starts. + uint8_t boot_count; + + /// The bootloader should set this to 0x23 for Tomu. + uint8_t board_model; + + /// Unused. + uint16_t reserved; +}; + +/// Set runtime.magic to this value and reboot to force +/// entry into Toboot. +#define TOBOOT_FORCE_ENTRY_MAGIC 0x74624346 + +#endif /* TOBOOT_API_H_ */ \ No newline at end of file diff --git a/sw/include/toboot-internal.h b/sw/include/toboot-internal.h new file mode 100644 index 0000000..9463766 --- /dev/null +++ b/sw/include/toboot-internal.h @@ -0,0 +1,49 @@ +#ifndef TOBOOT_INTERNAL_H_ +#define TOBOOT_INTERNAL_H_ + +#include + +/// This describes the structure that allows the OS to communicate +/// with the bootloader. It keeps track of how many times we've +/// tried booting, as well as a magic value that tells us to enter +/// the bootloader instead of booting the app. +/// It also keeps track of the board model. +__attribute__((section(".boot_token"))) extern struct toboot_runtime boot_token; + +enum bootloader_reason +{ + NOT_ENTERING_BOOTLOADER = 0, + BOOT_TOKEN_PRESENT = 1, + BOOT_FAILED_TOO_MANY_TIMES = 2, + NO_PROGRAM_PRESENT = 3, + BUTTON_HELD_DOWN = 4, + COLD_BOOT_CONFIGURATION_FLAG = 5, +}; + +extern enum bootloader_reason bootloader_reason; + +/// Legacy Toboot V1 configuration values +#ifndef TOBOOT_V1_CFG_FLAGS +#define TOBOOT_V1_CFG_FLAGS 0 +#endif +#define TOBOOT_V1_CFG_MAGIC_MASK 0xffff +#define TOBOOT_V1_CFG_MAGIC 0x70b0 + +#ifndef TOBOOT_V1_APP_FLAGS +#define TOBOOT_V1_APP_FLAGS 0 +#endif + +#define TOBOOT_V1_APP_MAGIC_MASK 0xffff +#define TOBOOT_V1_APP_MAGIC 0x6fb0 +#define TOBOOT_V1_APP_PAGE_MASK 0x00ff0000 +#define TOBOOT_V1_APP_PAGE_SHIFT 16 + +uint32_t tb_first_free_address(void); +uint32_t tb_first_free_sector(void); +const struct toboot_configuration *tb_get_config(void); +uint32_t tb_config_hash(const struct toboot_configuration *cfg); +void tb_sign_config(struct toboot_configuration *cfg); +uint32_t tb_generation(const struct toboot_configuration *cfg); +int tb_valid_signature_at_page(uint32_t page); + +#endif /* TOBOOT_INTERNAL_H_ */ \ No newline at end of file diff --git a/sw/include/usb-desc.h b/sw/include/usb-desc.h new file mode 100644 index 0000000..4a71da6 --- /dev/null +++ b/sw/include/usb-desc.h @@ -0,0 +1,95 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2013 PJRC.COM, LLC. + * + * 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. + */ + +#ifndef _usb_desc_h_ +#define _usb_desc_h_ + +#include +#include +#include +#include + +struct usb_setup_request { + union { + struct { + uint8_t bmRequestType; + uint8_t bRequest; + }; + uint16_t wRequestAndType; + }; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +}; + +struct usb_string_descriptor_struct { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wString[]; +}; + +#define NUM_USB_BUFFERS 8 +#define VENDOR_ID 0x1209 // pid.codes +#define PRODUCT_ID 0x70b1 // Assigned to Tomu project +#define DEVICE_VER 0x0101 // Bootloader version +#define MANUFACTURER_NAME u"Kosagi" +#define MANUFACTURER_NAME_LEN sizeof(MANUFACTURER_NAME) +#define PRODUCT_NAME u"Tomu Bootloader (0) " GIT_VERSION +#define PRODUCT_NAME_LEN sizeof(PRODUCT_NAME) +#define EP0_SIZE 64 +#define NUM_INTERFACE 1 +#define CONFIG_DESC_SIZE (9+9+9) + +// Microsoft Compatible ID Feature Descriptor +#define MSFT_VENDOR_CODE '~' // Arbitrary, but should be printable ASCII +#define MSFT_WCID_LEN 40 +extern const uint8_t usb_microsoft_wcid[MSFT_WCID_LEN]; + +typedef struct { + uint16_t wValue; + uint16_t length; + const uint8_t *addr; +} usb_descriptor_list_t; + +extern const usb_descriptor_list_t usb_descriptor_list[]; + +// WebUSB Landing page URL descriptor +#define WEBUSB_VENDOR_CODE 2 + +#ifndef LANDING_PAGE_URL +#define LANDING_PAGE_URL "dfu.tomu.im" +#endif + +#define LANDING_PAGE_DESCRIPTOR_SIZE (WEBUSB_DT_URL_DESCRIPTOR_SIZE \ + + sizeof(LANDING_PAGE_URL) - 1) + +extern const struct webusb_url_descriptor landing_url_descriptor; + +#endif diff --git a/sw/include/webusb-defs.h b/sw/include/webusb-defs.h new file mode 100644 index 0000000..fd699e9 --- /dev/null +++ b/sw/include/webusb-defs.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2016, Devan Lai + * + * Permission to use, copy, modify, and/or distribute this software + * for any purpose with or without fee is hereby granted, provided + * that the above copyright notice and this permission notice + * appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WEBUSB_DEFS_H_INCLUDED +#define WEBUSB_DEFS_H_INCLUDED + +#include + +#define WEBUSB_REQ_GET_URL 0x02 + +#define WEBUSB_DT_URL 3 + +#define WEBUSB_URL_SCHEME_HTTP 0 +#define WEBUSB_URL_SCHEME_HTTPS 1 + +struct webusb_platform_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDevCapabilityType; + uint8_t bReserved; + uint8_t platformCapabilityUUID[16]; + uint16_t bcdVersion; + uint8_t bVendorCode; + uint8_t iLandingPage; +} __attribute__((packed)); + +#define WEBUSB_PLATFORM_DESCRIPTOR_SIZE sizeof(struct webusb_platform_descriptor) + +#define WEBUSB_UUID {0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47,0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65} + +struct webusb_url_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bScheme; + char URL[]; +} __attribute__((packed)); + +#define WEBUSB_DT_URL_DESCRIPTOR_SIZE 3 + +#endif diff --git a/sw/include/xxhash.c b/sw/include/xxhash.c new file mode 100644 index 0000000..6f85fc4 --- /dev/null +++ b/sw/include/xxhash.c @@ -0,0 +1,912 @@ +/* +* xxHash - Fast Hash algorithm +* Copyright (C) 2012-2016, Yann Collet +* +* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are +* met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following disclaimer +* in the documentation and/or other materials provided with the +* distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* You can contact the author at : +* - xxHash homepage: http://www.xxhash.com +* - xxHash source repository : https://github.com/Cyan4973/xxHash +*/ + + +/* ************************************* +* Tuning parameters +***************************************/ +/*!XXH_FORCE_MEMORY_ACCESS : + * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. + * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. + * The below switch allow to select different access method for improved performance. + * Method 0 (default) : use `memcpy()`. Safe and portable. + * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). + * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. + * Method 2 : direct access. This method doesn't depend on compiler but violate C standard. + * It can generate buggy code on targets which do not support unaligned memory accesses. + * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) + * See http://stackoverflow.com/a/32095106/646947 for details. + * Prefer these methods in priority order (0 > 1 > 2) + */ +#ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ +# if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \ + || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) \ + || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) +# define XXH_FORCE_MEMORY_ACCESS 2 +# elif defined(__INTEL_COMPILER) || \ + (defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \ + || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) \ + || defined(__ARM_ARCH_7S__) )) +# define XXH_FORCE_MEMORY_ACCESS 1 +# endif +#endif + +/*!XXH_ACCEPT_NULL_INPUT_POINTER : + * If input pointer is NULL, xxHash default behavior is to dereference it, triggering a segfault. + * When this macro is enabled, xxHash actively checks input for null pointer. + * It it is, result for null input pointers is the same as a null-length input. + */ +#ifndef XXH_ACCEPT_NULL_INPUT_POINTER /* can be defined externally */ +# define XXH_ACCEPT_NULL_INPUT_POINTER 0 +#endif + +/*!XXH_FORCE_NATIVE_FORMAT : + * By default, xxHash library provides endian-independent Hash values, based on little-endian convention. + * Results are therefore identical for little-endian and big-endian CPU. + * This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format. + * Should endian-independence be of no importance for your application, you may set the #define below to 1, + * to improve speed for Big-endian CPU. + * This option has no impact on Little_Endian CPU. + */ +#ifndef XXH_FORCE_NATIVE_FORMAT /* can be defined externally */ +# define XXH_FORCE_NATIVE_FORMAT 0 +#endif + +/*!XXH_FORCE_ALIGN_CHECK : + * This is a minor performance trick, only useful with lots of very small keys. + * It means : check for aligned/unaligned input. + * The check costs one initial branch per hash; + * set it to 0 when the input is guaranteed to be aligned, + * or when alignment doesn't matter for performance. + */ +#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */ +# if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) +# define XXH_FORCE_ALIGN_CHECK 0 +# else +# define XXH_FORCE_ALIGN_CHECK 1 +# endif +#endif + + +/* ************************************* +* Includes & Memory related functions +***************************************/ +/*! Modify the local functions below should you wish to use some other memory routines +* for malloc(), free() */ +#include +static void* XXH_malloc(size_t s) { return malloc(s); } +static void XXH_free (void* p) { free(p); } +/*! and for memcpy() */ +#include +static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); } + +#define XXH_STATIC_LINKING_ONLY +#include "xxhash.h" + + +/* ************************************* +* Compiler Specific Options +***************************************/ +#ifdef _MSC_VER /* Visual Studio */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +# define FORCE_INLINE static __forceinline +#else +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# ifdef __GNUC__ +# define FORCE_INLINE static inline __attribute__((always_inline)) +# else +# define FORCE_INLINE static inline +# endif +# else +# define FORCE_INLINE static +# endif /* __STDC_VERSION__ */ +#endif + + +/* ************************************* +* Basic Types +***************************************/ +#ifndef MEM_MODULE +# if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + typedef uint8_t BYTE; + typedef uint16_t U16; + typedef uint32_t U32; +# else + typedef unsigned char BYTE; + typedef unsigned short U16; + typedef unsigned int U32; +# endif +#endif + +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) + +/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ +static U32 XXH_read32(const void* memPtr) { return *(const U32*) memPtr; } + +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) + +/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ +/* currently only defined for gcc and icc */ +typedef union { U32 u32; } __attribute__((packed)) unalign; +static U32 XXH_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } + +#else + +/* portable and safe solution. Generally efficient. + * see : http://stackoverflow.com/a/32095106/646947 + */ +static U32 XXH_read32(const void* memPtr) +{ + U32 val; + memcpy(&val, memPtr, sizeof(val)); + return val; +} + +#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ + + +/* **************************************** +* Compiler-specific Functions and Macros +******************************************/ +#define XXH_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) + +/* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */ +#if defined(_MSC_VER) +# define XXH_rotl32(x,r) _rotl(x,r) +# define XXH_rotl64(x,r) _rotl64(x,r) +#else +# define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r))) +# define XXH_rotl64(x,r) ((x << r) | (x >> (64 - r))) +#endif + +#if defined(_MSC_VER) /* Visual Studio */ +# define XXH_swap32 _byteswap_ulong +#elif XXH_GCC_VERSION >= 403 +# define XXH_swap32 __builtin_bswap32 +#else +static U32 XXH_swap32 (U32 x) +{ + return ((x << 24) & 0xff000000 ) | + ((x << 8) & 0x00ff0000 ) | + ((x >> 8) & 0x0000ff00 ) | + ((x >> 24) & 0x000000ff ); +} +#endif + + +/* ************************************* +* Architecture Macros +***************************************/ +typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess; + +/* XXH_CPU_LITTLE_ENDIAN can be defined externally, for example on the compiler command line */ +#ifndef XXH_CPU_LITTLE_ENDIAN + static const int g_one = 1; +# define XXH_CPU_LITTLE_ENDIAN (*(const char*)(&g_one)) +#endif + + +/* *************************** +* Memory reads +*****************************/ +typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment; + +FORCE_INLINE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_alignment align) +{ + if (align==XXH_unaligned) + return endian==XXH_littleEndian ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr)); + else + return endian==XXH_littleEndian ? *(const U32*)ptr : XXH_swap32(*(const U32*)ptr); +} + +FORCE_INLINE U32 XXH_readLE32(const void* ptr, XXH_endianess endian) +{ + return XXH_readLE32_align(ptr, endian, XXH_unaligned); +} + +static U32 XXH_readBE32(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr); +} + + +/* ************************************* +* Macros +***************************************/ +#define XXH_STATIC_ASSERT(c) { enum { XXH_sa = 1/(int)(!!(c)) }; } /* use after variable declarations */ +XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; } + + +/* ******************************************************************* +* 32-bit hash functions +*********************************************************************/ +static const U32 PRIME32_1 = 2654435761U; +static const U32 PRIME32_2 = 2246822519U; +static const U32 PRIME32_3 = 3266489917U; +static const U32 PRIME32_4 = 668265263U; +static const U32 PRIME32_5 = 374761393U; + +static U32 XXH32_round(U32 seed, U32 input) +{ + seed += input * PRIME32_2; + seed = XXH_rotl32(seed, 13); + seed *= PRIME32_1; + return seed; +} + +FORCE_INLINE U32 XXH32_endian_align(const void* input, size_t len, U32 seed, XXH_endianess endian, XXH_alignment align) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* bEnd = p + len; + U32 h32; +#define XXH_get32bits(p) XXH_readLE32_align(p, endian, align) + +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + if (p==NULL) { + len=0; + bEnd=p=(const BYTE*)(size_t)16; + } +#endif + + if (len>=16) { + const BYTE* const limit = bEnd - 16; + U32 v1 = seed + PRIME32_1 + PRIME32_2; + U32 v2 = seed + PRIME32_2; + U32 v3 = seed + 0; + U32 v4 = seed - PRIME32_1; + + do { + v1 = XXH32_round(v1, XXH_get32bits(p)); p+=4; + v2 = XXH32_round(v2, XXH_get32bits(p)); p+=4; + v3 = XXH32_round(v3, XXH_get32bits(p)); p+=4; + v4 = XXH32_round(v4, XXH_get32bits(p)); p+=4; + } while (p<=limit); + + h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18); + } else { + h32 = seed + PRIME32_5; + } + + h32 += (U32) len; + + while (p+4<=bEnd) { + h32 += XXH_get32bits(p) * PRIME32_3; + h32 = XXH_rotl32(h32, 17) * PRIME32_4 ; + p+=4; + } + + while (p> 15; + h32 *= PRIME32_2; + h32 ^= h32 >> 13; + h32 *= PRIME32_3; + h32 ^= h32 >> 16; + + return h32; +} + + +XXH_PUBLIC_API unsigned int XXH32 (const void* input, size_t len, unsigned int seed) +{ +#if 0 + /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ + XXH32_state_t state; + XXH32_reset(&state, seed); + XXH32_update(&state, input, len); + return XXH32_digest(&state); +#else + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if (XXH_FORCE_ALIGN_CHECK) { + if ((((size_t)input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */ + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); + else + return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); + } } + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned); + else + return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); +#endif +} + + + +/*====== Hash streaming ======*/ + +XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void) +{ + return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t)); +} +XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr) +{ + XXH_free(statePtr); + return XXH_OK; +} + +XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState) +{ + memcpy(dstState, srcState, sizeof(*dstState)); +} + +XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, unsigned int seed) +{ + XXH32_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ + memset(&state, 0, sizeof(state)); + state.v1 = seed + PRIME32_1 + PRIME32_2; + state.v2 = seed + PRIME32_2; + state.v3 = seed + 0; + state.v4 = seed - PRIME32_1; + /* do not write into reserved, planned to be removed in a future version */ + memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved)); + return XXH_OK; +} + + +FORCE_INLINE +XXH_errorcode XXH32_update_endian (XXH32_state_t* state, const void* input, size_t len, XXH_endianess endian) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* const bEnd = p + len; + + if (input==NULL) +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + return XXH_OK; +#else + return XXH_ERROR; +#endif + + state->total_len_32 += (unsigned)len; + state->large_len |= (len>=16) | (state->total_len_32>=16); + + if (state->memsize + len < 16) { /* fill in tmp buffer */ + XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, len); + state->memsize += (unsigned)len; + return XXH_OK; + } + + if (state->memsize) { /* some data left from previous update */ + XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, 16-state->memsize); + { const unsigned* p32 = state->mem32; + state->v1 = XXH32_round(state->v1, XXH_readLE32(p32, endian)); p32++; + state->v2 = XXH32_round(state->v2, XXH_readLE32(p32, endian)); p32++; + state->v3 = XXH32_round(state->v3, XXH_readLE32(p32, endian)); p32++; + state->v4 = XXH32_round(state->v4, XXH_readLE32(p32, endian)); + } + p += 16-state->memsize; + state->memsize = 0; + } + + if (p <= bEnd-16) { + const BYTE* const limit = bEnd - 16; + U32 v1 = state->v1; + U32 v2 = state->v2; + U32 v3 = state->v3; + U32 v4 = state->v4; + + do { + v1 = XXH32_round(v1, XXH_readLE32(p, endian)); p+=4; + v2 = XXH32_round(v2, XXH_readLE32(p, endian)); p+=4; + v3 = XXH32_round(v3, XXH_readLE32(p, endian)); p+=4; + v4 = XXH32_round(v4, XXH_readLE32(p, endian)); p+=4; + } while (p<=limit); + + state->v1 = v1; + state->v2 = v2; + state->v3 = v3; + state->v4 = v4; + } + + if (p < bEnd) { + XXH_memcpy(state->mem32, p, (size_t)(bEnd-p)); + state->memsize = (unsigned)(bEnd-p); + } + + return XXH_OK; +} + +XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* state_in, const void* input, size_t len) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_update_endian(state_in, input, len, XXH_littleEndian); + else + return XXH32_update_endian(state_in, input, len, XXH_bigEndian); +} + + + +FORCE_INLINE U32 XXH32_digest_endian (const XXH32_state_t* state, XXH_endianess endian) +{ + const BYTE * p = (const BYTE*)state->mem32; + const BYTE* const bEnd = (const BYTE*)(state->mem32) + state->memsize; + U32 h32; + + if (state->large_len) { + h32 = XXH_rotl32(state->v1, 1) + + XXH_rotl32(state->v2, 7) + + XXH_rotl32(state->v3, 12) + + XXH_rotl32(state->v4, 18); + } else { + h32 = state->v3 /* == seed */ + PRIME32_5; + } + + h32 += state->total_len_32; + + while (p+4<=bEnd) { + h32 += XXH_readLE32(p, endian) * PRIME32_3; + h32 = XXH_rotl32(h32, 17) * PRIME32_4; + p+=4; + } + + while (p> 15; + h32 *= PRIME32_2; + h32 ^= h32 >> 13; + h32 *= PRIME32_3; + h32 ^= h32 >> 16; + + return h32; +} + + +XXH_PUBLIC_API unsigned int XXH32_digest (const XXH32_state_t* state_in) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_digest_endian(state_in, XXH_littleEndian); + else + return XXH32_digest_endian(state_in, XXH_bigEndian); +} + + +/*====== Canonical representation ======*/ + +/*! Default XXH result types are basic unsigned 32 and 64 bits. +* The canonical representation follows human-readable write convention, aka big-endian (large digits first). +* These functions allow transformation of hash result into and from its canonical format. +* This way, hash values can be written into a file or buffer, remaining comparable across different systems. +*/ + +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash) +{ + XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash); + memcpy(dst, &hash, sizeof(*dst)); +} + +XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src) +{ + return XXH_readBE32(src); +} + + +#ifndef XXH_NO_LONG_LONG + +/* ******************************************************************* +* 64-bit hash functions +*********************************************************************/ + +/*====== Memory access ======*/ + +#ifndef MEM_MODULE +# define MEM_MODULE +# if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + typedef uint64_t U64; +# else + /* if compiler doesn't support unsigned long long, replace by another 64-bit type */ + typedef unsigned long long U64; +# endif +#endif + + +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) + +/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ +static U64 XXH_read64(const void* memPtr) { return *(const U64*) memPtr; } + +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) + +/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ +/* currently only defined for gcc and icc */ +typedef union { U32 u32; U64 u64; } __attribute__((packed)) unalign64; +static U64 XXH_read64(const void* ptr) { return ((const unalign64*)ptr)->u64; } + +#else + +/* portable and safe solution. Generally efficient. + * see : http://stackoverflow.com/a/32095106/646947 + */ + +static U64 XXH_read64(const void* memPtr) +{ + U64 val; + memcpy(&val, memPtr, sizeof(val)); + return val; +} + +#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ + +#if defined(_MSC_VER) /* Visual Studio */ +# define XXH_swap64 _byteswap_uint64 +#elif XXH_GCC_VERSION >= 403 +# define XXH_swap64 __builtin_bswap64 +#else +static U64 XXH_swap64 (U64 x) +{ + return ((x << 56) & 0xff00000000000000ULL) | + ((x << 40) & 0x00ff000000000000ULL) | + ((x << 24) & 0x0000ff0000000000ULL) | + ((x << 8) & 0x000000ff00000000ULL) | + ((x >> 8) & 0x00000000ff000000ULL) | + ((x >> 24) & 0x0000000000ff0000ULL) | + ((x >> 40) & 0x000000000000ff00ULL) | + ((x >> 56) & 0x00000000000000ffULL); +} +#endif + +FORCE_INLINE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_alignment align) +{ + if (align==XXH_unaligned) + return endian==XXH_littleEndian ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr)); + else + return endian==XXH_littleEndian ? *(const U64*)ptr : XXH_swap64(*(const U64*)ptr); +} + +FORCE_INLINE U64 XXH_readLE64(const void* ptr, XXH_endianess endian) +{ + return XXH_readLE64_align(ptr, endian, XXH_unaligned); +} + +static U64 XXH_readBE64(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr); +} + + +/*====== xxh64 ======*/ + +static const U64 PRIME64_1 = 11400714785074694791ULL; +static const U64 PRIME64_2 = 14029467366897019727ULL; +static const U64 PRIME64_3 = 1609587929392839161ULL; +static const U64 PRIME64_4 = 9650029242287828579ULL; +static const U64 PRIME64_5 = 2870177450012600261ULL; + +static U64 XXH64_round(U64 acc, U64 input) +{ + acc += input * PRIME64_2; + acc = XXH_rotl64(acc, 31); + acc *= PRIME64_1; + return acc; +} + +static U64 XXH64_mergeRound(U64 acc, U64 val) +{ + val = XXH64_round(0, val); + acc ^= val; + acc = acc * PRIME64_1 + PRIME64_4; + return acc; +} + +FORCE_INLINE U64 XXH64_endian_align(const void* input, size_t len, U64 seed, XXH_endianess endian, XXH_alignment align) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* bEnd = p + len; + U64 h64; +#define XXH_get64bits(p) XXH_readLE64_align(p, endian, align) + +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + if (p==NULL) { + len=0; + bEnd=p=(const BYTE*)(size_t)32; + } +#endif + + if (len>=32) { + const BYTE* const limit = bEnd - 32; + U64 v1 = seed + PRIME64_1 + PRIME64_2; + U64 v2 = seed + PRIME64_2; + U64 v3 = seed + 0; + U64 v4 = seed - PRIME64_1; + + do { + v1 = XXH64_round(v1, XXH_get64bits(p)); p+=8; + v2 = XXH64_round(v2, XXH_get64bits(p)); p+=8; + v3 = XXH64_round(v3, XXH_get64bits(p)); p+=8; + v4 = XXH64_round(v4, XXH_get64bits(p)); p+=8; + } while (p<=limit); + + h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); + h64 = XXH64_mergeRound(h64, v1); + h64 = XXH64_mergeRound(h64, v2); + h64 = XXH64_mergeRound(h64, v3); + h64 = XXH64_mergeRound(h64, v4); + + } else { + h64 = seed + PRIME64_5; + } + + h64 += (U64) len; + + while (p+8<=bEnd) { + U64 const k1 = XXH64_round(0, XXH_get64bits(p)); + h64 ^= k1; + h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4; + p+=8; + } + + if (p+4<=bEnd) { + h64 ^= (U64)(XXH_get32bits(p)) * PRIME64_1; + h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; + p+=4; + } + + while (p> 33; + h64 *= PRIME64_2; + h64 ^= h64 >> 29; + h64 *= PRIME64_3; + h64 ^= h64 >> 32; + + return h64; +} + + +XXH_PUBLIC_API unsigned long long XXH64 (const void* input, size_t len, unsigned long long seed) +{ +#if 0 + /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ + XXH64_state_t state; + XXH64_reset(&state, seed); + XXH64_update(&state, input, len); + return XXH64_digest(&state); +#else + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if (XXH_FORCE_ALIGN_CHECK) { + if ((((size_t)input) & 7)==0) { /* Input is aligned, let's leverage the speed advantage */ + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); + else + return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); + } } + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned); + else + return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); +#endif +} + +/*====== Hash Streaming ======*/ + +XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void) +{ + return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t)); +} +XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr) +{ + XXH_free(statePtr); + return XXH_OK; +} + +XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dstState, const XXH64_state_t* srcState) +{ + memcpy(dstState, srcState, sizeof(*dstState)); +} + +XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, unsigned long long seed) +{ + XXH64_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ + memset(&state, 0, sizeof(state)); + state.v1 = seed + PRIME64_1 + PRIME64_2; + state.v2 = seed + PRIME64_2; + state.v3 = seed + 0; + state.v4 = seed - PRIME64_1; + /* do not write into reserved, planned to be removed in a future version */ + memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved)); + return XXH_OK; +} + +FORCE_INLINE +XXH_errorcode XXH64_update_endian (XXH64_state_t* state, const void* input, size_t len, XXH_endianess endian) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* const bEnd = p + len; + + if (input==NULL) +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + return XXH_OK; +#else + return XXH_ERROR; +#endif + + state->total_len += len; + + if (state->memsize + len < 32) { /* fill in tmp buffer */ + XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len); + state->memsize += (U32)len; + return XXH_OK; + } + + if (state->memsize) { /* tmp buffer is full */ + XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, 32-state->memsize); + state->v1 = XXH64_round(state->v1, XXH_readLE64(state->mem64+0, endian)); + state->v2 = XXH64_round(state->v2, XXH_readLE64(state->mem64+1, endian)); + state->v3 = XXH64_round(state->v3, XXH_readLE64(state->mem64+2, endian)); + state->v4 = XXH64_round(state->v4, XXH_readLE64(state->mem64+3, endian)); + p += 32-state->memsize; + state->memsize = 0; + } + + if (p+32 <= bEnd) { + const BYTE* const limit = bEnd - 32; + U64 v1 = state->v1; + U64 v2 = state->v2; + U64 v3 = state->v3; + U64 v4 = state->v4; + + do { + v1 = XXH64_round(v1, XXH_readLE64(p, endian)); p+=8; + v2 = XXH64_round(v2, XXH_readLE64(p, endian)); p+=8; + v3 = XXH64_round(v3, XXH_readLE64(p, endian)); p+=8; + v4 = XXH64_round(v4, XXH_readLE64(p, endian)); p+=8; + } while (p<=limit); + + state->v1 = v1; + state->v2 = v2; + state->v3 = v3; + state->v4 = v4; + } + + if (p < bEnd) { + XXH_memcpy(state->mem64, p, (size_t)(bEnd-p)); + state->memsize = (unsigned)(bEnd-p); + } + + return XXH_OK; +} + +XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* state_in, const void* input, size_t len) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_update_endian(state_in, input, len, XXH_littleEndian); + else + return XXH64_update_endian(state_in, input, len, XXH_bigEndian); +} + +FORCE_INLINE U64 XXH64_digest_endian (const XXH64_state_t* state, XXH_endianess endian) +{ + const BYTE * p = (const BYTE*)state->mem64; + const BYTE* const bEnd = (const BYTE*)state->mem64 + state->memsize; + U64 h64; + + if (state->total_len >= 32) { + U64 const v1 = state->v1; + U64 const v2 = state->v2; + U64 const v3 = state->v3; + U64 const v4 = state->v4; + + h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); + h64 = XXH64_mergeRound(h64, v1); + h64 = XXH64_mergeRound(h64, v2); + h64 = XXH64_mergeRound(h64, v3); + h64 = XXH64_mergeRound(h64, v4); + } else { + h64 = state->v3 + PRIME64_5; + } + + h64 += (U64) state->total_len; + + while (p+8<=bEnd) { + U64 const k1 = XXH64_round(0, XXH_readLE64(p, endian)); + h64 ^= k1; + h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4; + p+=8; + } + + if (p+4<=bEnd) { + h64 ^= (U64)(XXH_readLE32(p, endian)) * PRIME64_1; + h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; + p+=4; + } + + while (p> 33; + h64 *= PRIME64_2; + h64 ^= h64 >> 29; + h64 *= PRIME64_3; + h64 ^= h64 >> 32; + + return h64; +} + +XXH_PUBLIC_API unsigned long long XXH64_digest (const XXH64_state_t* state_in) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_digest_endian(state_in, XXH_littleEndian); + else + return XXH64_digest_endian(state_in, XXH_bigEndian); +} + + +/*====== Canonical representation ======*/ + +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash) +{ + XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash); + memcpy(dst, &hash, sizeof(*dst)); +} + +XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src) +{ + return XXH_readBE64(src); +} + +#endif /* XXH_NO_LONG_LONG */ diff --git a/sw/include/xxhash.h b/sw/include/xxhash.h new file mode 100644 index 0000000..4099da0 --- /dev/null +++ b/sw/include/xxhash.h @@ -0,0 +1,294 @@ +/* + xxHash - Extremely Fast Hash algorithm + Header File + Copyright (C) 2012-2016, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - xxHash source repository : https://github.com/Cyan4973/xxHash +*/ + +/* Notice extracted from xxHash homepage : + +xxHash is an extremely fast Hash algorithm, running at RAM speed limits. +It also successfully passes all tests from the SMHasher suite. + +Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz) + +Name Speed Q.Score Author +xxHash 5.4 GB/s 10 +CrapWow 3.2 GB/s 2 Andrew +MumurHash 3a 2.7 GB/s 10 Austin Appleby +SpookyHash 2.0 GB/s 10 Bob Jenkins +SBox 1.4 GB/s 9 Bret Mulvey +Lookup3 1.2 GB/s 9 Bob Jenkins +SuperFastHash 1.2 GB/s 1 Paul Hsieh +CityHash64 1.05 GB/s 10 Pike & Alakuijala +FNV 0.55 GB/s 5 Fowler, Noll, Vo +CRC32 0.43 GB/s 9 +MD5-32 0.33 GB/s 10 Ronald L. Rivest +SHA1-32 0.28 GB/s 10 + +Q.Score is a measure of quality of the hash function. +It depends on successfully passing SMHasher test set. +10 is a perfect score. + +A 64-bit version, named XXH64, is available since r35. +It offers much better speed, but for 64-bit applications only. +Name Speed on 64 bits Speed on 32 bits +XXH64 13.8 GB/s 1.9 GB/s +XXH32 6.8 GB/s 6.0 GB/s +*/ + +#ifndef XXHASH_H_5627135585666179 +#define XXHASH_H_5627135585666179 1 + +#if defined (__cplusplus) +extern "C" { +#endif + + +/* **************************** +* Definitions +******************************/ +#include /* size_t */ +typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode; + + +/* **************************** +* API modifier +******************************/ +/** XXH_PRIVATE_API +* This is useful to include xxhash functions in `static` mode +* in order to inline them, and remove their symbol from the public list. +* Methodology : +* #define XXH_PRIVATE_API +* #include "xxhash.h" +* `xxhash.c` is automatically included. +* It's not useful to compile and link it as a separate module. +*/ +#ifdef XXH_PRIVATE_API +# ifndef XXH_STATIC_LINKING_ONLY +# define XXH_STATIC_LINKING_ONLY +# endif +# if defined(__GNUC__) +# define XXH_PUBLIC_API static __inline __attribute__((unused)) +# elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) +# define XXH_PUBLIC_API static inline +# elif defined(_MSC_VER) +# define XXH_PUBLIC_API static __inline +# else + /* this version may generate warnings for unused static functions */ +# define XXH_PUBLIC_API static +# endif +#else +# define XXH_PUBLIC_API /* do nothing */ +#endif /* XXH_PRIVATE_API */ + +/*!XXH_NAMESPACE, aka Namespace Emulation : + +If you want to include _and expose_ xxHash functions from within your own library, +but also want to avoid symbol collisions with other libraries which may also include xxHash, + +you can use XXH_NAMESPACE, to automatically prefix any public symbol from xxhash library +with the value of XXH_NAMESPACE (therefore, avoid NULL and numeric values). + +Note that no change is required within the calling program as long as it includes `xxhash.h` : +regular symbol name will be automatically translated by this header. +*/ +#ifdef XXH_NAMESPACE +# define XXH_CAT(A,B) A##B +# define XXH_NAME2(A,B) XXH_CAT(A,B) +# define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber) +# define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32) +# define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState) +# define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState) +# define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset) +# define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update) +# define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest) +# define XXH32_copyState XXH_NAME2(XXH_NAMESPACE, XXH32_copyState) +# define XXH32_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash) +# define XXH32_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical) +# define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64) +# define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState) +# define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState) +# define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset) +# define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update) +# define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest) +# define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState) +# define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash) +# define XXH64_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical) +#endif + + +/* ************************************* +* Version +***************************************/ +#define XXH_VERSION_MAJOR 0 +#define XXH_VERSION_MINOR 6 +#define XXH_VERSION_RELEASE 4 +#define XXH_VERSION_NUMBER (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE) +XXH_PUBLIC_API unsigned XXH_versionNumber (void); + + +/*-********************************************************************** +* 32-bit hash +************************************************************************/ +typedef unsigned int XXH32_hash_t; + +/*! XXH32() : + Calculate the 32-bit hash of sequence "length" bytes stored at memory address "input". + The memory between input & input+length must be valid (allocated and read-accessible). + "seed" can be used to alter the result predictably. + Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s */ +XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, unsigned int seed); + +/*====== Streaming ======*/ +typedef struct XXH32_state_s XXH32_state_t; /* incomplete type */ +XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void); +XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr); +XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state); + +XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, unsigned int seed); +XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length); +XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr); + +/* +These functions generate the xxHash of an input provided in multiple segments. +Note that, for small input, they are slower than single-call functions, due to state management. +For small input, prefer `XXH32()` and `XXH64()` . + +XXH state must first be allocated, using XXH*_createState() . + +Start a new hash by initializing state with a seed, using XXH*_reset(). + +Then, feed the hash state by calling XXH*_update() as many times as necessary. +Obviously, input must be allocated and read accessible. +The function returns an error code, with 0 meaning OK, and any other value meaning there is an error. + +Finally, a hash value can be produced anytime, by using XXH*_digest(). +This function returns the nn-bits hash as an int or long long. + +It's still possible to continue inserting input into the hash state after a digest, +and generate some new hashes later on, by calling again XXH*_digest(). + +When done, free XXH state space if it was allocated dynamically. +*/ + +/*====== Canonical representation ======*/ + +typedef struct { unsigned char digest[4]; } XXH32_canonical_t; +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash); +XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src); + +/* Default result type for XXH functions are primitive unsigned 32 and 64 bits. +* The canonical representation uses human-readable write convention, aka big-endian (large digits first). +* These functions allow transformation of hash result into and from its canonical format. +* This way, hash values can be written into a file / memory, and remain comparable on different systems and programs. +*/ + + +#ifndef XXH_NO_LONG_LONG +/*-********************************************************************** +* 64-bit hash +************************************************************************/ +typedef unsigned long long XXH64_hash_t; + +/*! XXH64() : + Calculate the 64-bit hash of sequence of length "len" stored at memory address "input". + "seed" can be used to alter the result predictably. + This function runs faster on 64-bit systems, but slower on 32-bit systems (see benchmark). +*/ +XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t length, unsigned long long seed); + +/*====== Streaming ======*/ +typedef struct XXH64_state_s XXH64_state_t; /* incomplete type */ +XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void); +XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr); +XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dst_state, const XXH64_state_t* src_state); + +XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH64_state_t* statePtr, unsigned long long seed); +XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length); +XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* statePtr); + +/*====== Canonical representation ======*/ +typedef struct { unsigned char digest[8]; } XXH64_canonical_t; +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash); +XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src); +#endif /* XXH_NO_LONG_LONG */ + + +#ifdef XXH_STATIC_LINKING_ONLY + +/* ================================================================================================ + This section contains declarations which are not guaranteed to remain stable. + They may change in future versions, becoming incompatible with a different version of the library. + These declarations should only be used with static linking. + Never use them in association with dynamic linking ! +=================================================================================================== */ + +/* These definitions are only meant to make possible + static allocation of XXH state, on stack or in a struct for example. + Never use members directly. */ + +struct XXH32_state_s { + unsigned total_len_32; + unsigned large_len; + unsigned v1; + unsigned v2; + unsigned v3; + unsigned v4; + unsigned mem32[4]; /* buffer defined as U32 for alignment */ + unsigned memsize; + unsigned reserved; /* never read nor write, will be removed in a future version */ +}; /* typedef'd to XXH32_state_t */ + +#ifndef XXH_NO_LONG_LONG /* remove 64-bit support */ +struct XXH64_state_s { + unsigned long long total_len; + unsigned long long v1; + unsigned long long v2; + unsigned long long v3; + unsigned long long v4; + unsigned long long mem64[4]; /* buffer defined as U64 for alignment */ + unsigned memsize; + unsigned reserved[2]; /* never read nor write, will be removed in a future version */ +}; /* typedef'd to XXH64_state_t */ +#endif + +#ifdef XXH_PRIVATE_API +# include "xxhash.c" /* include xxhash function bodies as `static`, for inlining */ +#endif + +#endif /* XXH_STATIC_LINKING_ONLY */ + + +#if defined (__cplusplus) +} +#endif + +#endif /* XXHASH_H_5627135585666179 */ diff --git a/sw/src/dfu.c b/sw/src/dfu.c new file mode 100644 index 0000000..23dbcf5 --- /dev/null +++ b/sw/src/dfu.c @@ -0,0 +1,500 @@ +/* + * Fadecandy DFU Bootloader + * + * Copyright (c) 2013 Micah Elizabeth Scott + * + * 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. + */ + +#include +#include + +#include +#include +#include + +// Internal flash-programming state machine +static unsigned fl_current_addr = 0; +static enum { + flsIDLE = 0, + flsERASING, + flsPROGRAMMING +} fl_state; + +static struct toboot_state { + // Version number of the program being loaded: + // 0 (legacy) + // 1 (toboot v1) + // 2 (toboot v2) + uint8_t version; + + // When clearing, first ensure these sectors are cleared prior to updating + uint32_t clear_lo; + uint32_t clear_hi; + + // The current block we're clearing + uint32_t clear_current; + + // This is the address we'll start programming/erasing from after clearing + uint32_t next_addr; + + enum { + /// Toboot has just started + tbsIDLE, + + /// Secure erase memory is being cleared + tbsCLEARING, + + /// New image is being loaded + tbsLOADING, + } state; +} tb_state; + +static dfu_state_t dfu_state = dfuIDLE; +static dfu_status_t dfu_status = OK; +static unsigned dfu_poll_timeout = 1; + +static uint32_t dfu_buffer[DFU_TRANSFER_SIZE/4]; +static uint32_t dfu_buffer_offset; +static uint32_t fl_num_words; + +// Memory offset we're uploading to. +static uint32_t dfu_target_address; + +static void set_state(dfu_state_t new_state, dfu_status_t new_status) { + dfu_state = new_state; + dfu_status = new_status; +} + +bool fl_is_idle(void) { + return fl_state == flsIDLE; +} + +/* +void *memcpy(void *dst, const void *src, size_t cnt) { + uint8_t *dst8 = dst; + const uint8_t *src8 = src; + while (cnt > 0) { + cnt--; + *(dst8++) = *(src8++); + } + return dst; +} +*/ + +static bool ftfl_busy() +{ + // Is the flash memory controller busy? + // return (MSC->STATUS & MSC_STATUS_BUSY); + return 0; +} + +static void ftfl_busy_wait() +{ + // Wait for the flash memory controller to finish any pending operation. + while (ftfl_busy()) + ;//watchdog_refresh(); +} + +static void ftfl_begin_erase_sector(uint32_t address) +{ + // Erase the page at the specified address. + //MSC->WRITECTRL |= MSC_WRITECTRL_WREN; + + ftfl_busy_wait(); + //MSC->ADDRB = address; + //MSC->WRITECMD = MSC_WRITECMD_LADDRIM; + //MSC->WRITECMD = MSC_WRITECMD_ERASEPAGE; +} + +static void ftfl_begin_program_section(uint32_t address) +{ + // Write the buffer word to the currently selected address. + // Note that after this is done, the address is incremented by 4. + dfu_buffer_offset = 0; + dfu_target_address = address; + fl_num_words--; +ftfl_busy_wait(); + //MSC->ADDRB = address; +ftfl_busy_wait(); + //MSC->WRITECTRL |= MSC_WRITECTRL_WREN; + //MSC->WDATA = dfu_buffer[dfu_buffer_offset++]; + //MSC->WRITECMD = MSC_WRITECMD_WRITEONCE; +} + +static uint32_t address_for_block(unsigned blockNum) +{ + static uint32_t starting_offset; + if (blockNum == 0) { + // Determine Toboot version. + if ((dfu_buffer[0x94 / 4] & TOBOOT_V2_MAGIC_MASK) == TOBOOT_V2_MAGIC) { + tb_state.version = 2; + starting_offset = ((struct toboot_configuration *)&dfu_buffer[0x94 / 4])->start; + } + // V1 used a different offset. + else if ((dfu_buffer[0x98 / 4] & TOBOOT_V1_MAGIC_MASK) == TOBOOT_V1_MAGIC) { + // Applications that know about Toboot will indicate their block + // offset by placing a magic byte at offset 0x98. + // Ordinarily this would be the address offset for IRQ 22, + // but since there are only 20 IRQs on the EFM32HG, there are three + // 32-bit values that are unused starting at offset 0x94. + // We already use offset 0x94 for "disable boot", so use offset 0x98 + // in the incoming stream to indicate flags for Toboot. + tb_state.version = 1; + starting_offset = (dfu_buffer[0x98 / 4] & TOBOOT_V1_APP_PAGE_MASK) >> TOBOOT_V1_APP_PAGE_SHIFT; + } + // Legacy programs default to offset 0x4000. + else { + tb_state.version = 0; + starting_offset = 16; + } + + // Set the state to "CLEARING", since we're just starting the programming process. + tb_state.state = tbsCLEARING; + starting_offset *= 0x400; + } + return starting_offset + (blockNum << 10); +} + +// If requested, erase sectors before loading new code. +static void pre_clear_next_block(void) { + + // If there is another sector to clear, do that. + while (++tb_state.clear_current < 64) { + if (tb_state.clear_current < 32) { + if ((tb_state.clear_lo & (1 << tb_state.clear_current))) { + ftfl_begin_erase_sector(tb_state.clear_current * 1024); + return; + } + } + else if (tb_state.clear_current < 64) { + if ((tb_state.clear_hi & (1 << (tb_state.clear_current & 31)))) { + ftfl_begin_erase_sector(tb_state.clear_current * 1024); + return; + } + } + } + + // No more sectors to clear, continue with programming + tb_state.state = tbsLOADING; + ftfl_begin_erase_sector(tb_state.next_addr); +} + +void dfu_init(void) +{ + tb_state.state = tbsIDLE; +/* + // Ensure the clocks for the memory are enabled + CMU->OSCENCMD = CMU_OSCENCMD_AUXHFRCOEN; + while (!(CMU->STATUS & CMU_STATUS_AUXHFRCORDY)) + ; + + // Unlock the MSC + MSC->LOCK = MSC_UNLOCK_CODE; + + // Enable writing to flash + MSC->WRITECTRL |= MSC_WRITECTRL_WREN; + MSC->IEN |= MSC_IEN_WRITE | MSC_IEN_ERASE; + NVIC_EnableIRQ(MSC_IRQn); +*/ +} + +uint8_t dfu_getstate(void) +{ + return dfu_state; +} + +bool dfu_download(unsigned blockNum, unsigned blockLength, + unsigned packetOffset, unsigned packetLength, const uint8_t *data) +{ + uint32_t i; + + if (packetOffset + packetLength > DFU_TRANSFER_SIZE || + packetOffset + packetLength > blockLength) { + + // Overflow! + set_state(dfuERROR, errADDRESS); + return false; + } + + // Store more data... + memcpy(((uint8_t *)dfu_buffer) + packetOffset, data, packetLength); + + if (packetOffset + packetLength != blockLength) { + // Still waiting for more data. + return true; + } + + if (dfu_state != dfuIDLE && dfu_state != dfuDNLOAD_IDLE) { + // Wrong state! Oops. + set_state(dfuERROR, errSTALLEDPKT); + return false; + } + + if (ftfl_busy() || fl_state != flsIDLE) { + // Flash controller shouldn't be busy now! + set_state(dfuERROR, errUNKNOWN); + return false; + } + + if (!blockLength) { + // End of download + set_state(dfuMANIFEST_SYNC, OK); + return true; + } + + // Start programming a block by erasing the corresponding flash sector + fl_state = flsERASING; + fl_current_addr = address_for_block(blockNum); + fl_num_words = blockLength / 4; + + // If it's the first block, figure out what we need to do in terms of erasing + // data and programming the new file. + if (blockNum == 0) { + const struct toboot_configuration *old_config = tb_get_config(); + + // Don't allow overwriting Toboot itself. + if (fl_current_addr < tb_first_free_address()) { + set_state(dfuERROR, errADDRESS); + return false; + } + + // Calculate generation number and hash + if (tb_state.version == 2) { + struct toboot_configuration *new_config = (struct toboot_configuration *)&dfu_buffer[0x94 / 4]; + + // Update generation number + new_config->reserved_gen = old_config->reserved_gen + 1; + + // Ensure we know this header is not fake + new_config->config &= ~TOBOOT_CONFIG_FAKE; + + // Generate a valid signature + tb_sign_config(new_config); + } + + // If the old configuration requires that certain blocks be erased, do that. + tb_state.clear_hi = old_config->erase_mask_hi; + tb_state.clear_lo = old_config->erase_mask_lo; + tb_state.clear_current = 0; + + // Ensure we don't erase Toboot itself + for (i = 0; i < tb_first_free_sector(); i++) { + if (i < 32) + tb_state.clear_lo &= ~(1 << i); + else + tb_state.clear_hi &= ~(1 << i); + } + + // If the newly-loaded program does not conform to Toboot V2.0, then look + // for any existing programs on the flash and delete those sectors. + // Because of boot priority, we need to ensure that no V2.0 applications + // exist on flash. + if (tb_state.version < 2) { + for (i = tb_first_free_sector(); i < 64; i++) { + if (tb_valid_signature_at_page(i) < 0) + continue; + if (i < 32) + tb_state.clear_lo |= (1 << i); + else + tb_state.clear_hi |= (1 << (i - 32)); + } + } + + // If we still have sectors to clear, do that. Otherwise, + // go straight into loading the program. + if (tb_state.clear_lo || tb_state.clear_hi) { + tb_state.state = tbsCLEARING; + tb_state.next_addr = fl_current_addr; + pre_clear_next_block(); + } + else { + tb_state.state = tbsLOADING; + ftfl_begin_erase_sector(fl_current_addr); + } + } + else + ftfl_begin_erase_sector(fl_current_addr); + + set_state(dfuDNLOAD_SYNC, OK); + return true; +} + +static bool fl_handle_status(uint8_t fstat) +{ + /* + * Handle common errors from an FSTAT register value. + * The indicated "specificError" is used for reporting a command-specific + * error from MGSTAT0. + * + * Returns true if handled, false if not. + */ +#if 0 + if (fstat & MSC_STATUS_BUSY) { + // Still working... + return true; + } + + if (fstat & (MSC_STATUS_ERASEABORTED | MSC_STATUS_WORDTIMEOUT)) { + // Bus collision. We did something wrong internally. + set_state(dfuERROR, errUNKNOWN); + fl_state = flsIDLE; + return true; + } + + if (fstat & (MSC_STATUS_INVADDR | MSC_STATUS_LOCKED)) { + // Address or protection error + set_state(dfuERROR, errADDRESS); + fl_state = flsIDLE; + return true; + } + + if (fl_state == flsPROGRAMMING) { + // Still programming... + return true; + } +#endif + return false; +} + +static void fl_state_poll(void) +{ + // Try to advance the state of our own flash programming state machine. + + uint32_t fstat = 0;//MSC->STATUS; + + switch (fl_state) { + + case flsIDLE: + break; + + case flsERASING: + if (!fl_handle_status(fstat)) { + // ?If we're still pre-clearing, continue with that. + if (tb_state.state == tbsCLEARING) { + pre_clear_next_block(); + } + // Done! Move on to programming the sector. + else { + fl_state = flsPROGRAMMING; + ftfl_begin_program_section(fl_current_addr); + } + } + break; + + case flsPROGRAMMING: + if (!fl_handle_status(fstat)) { + // Done! + fl_state = flsIDLE; + } + break; + } +} + +bool dfu_getstatus(uint8_t status[8]) +{ + switch (dfu_state) { + + case dfuDNLOAD_SYNC: + case dfuDNBUSY: + // Programming operation in progress. Advance our private flash state machine. + fl_state_poll(); + + if (dfu_state == dfuERROR) { + // An error occurred inside fl_state_poll(); + } else if (fl_state == flsIDLE) { + dfu_state = dfuDNLOAD_IDLE; + } else { + dfu_state = dfuDNBUSY; + } + break; + + case dfuMANIFEST_SYNC: + // Ready to reboot. The main thread will take care of this. Also let the DFU tool + // know to leave us alone until this happens. + dfu_state = dfuMANIFEST; + dfu_poll_timeout = 10; + break; + + case dfuMANIFEST: + // Perform the reboot + dfu_state = dfuMANIFEST_WAIT_RESET; + dfu_poll_timeout = 1000; + break; + + default: + break; + } + + status[0] = dfu_status; + status[1] = dfu_poll_timeout; + status[2] = dfu_poll_timeout >> 8; + status[3] = dfu_poll_timeout >> 16; + status[4] = dfu_state; + status[5] = 0; // iString + + return true; +} + +bool dfu_clrstatus(void) +{ + switch (dfu_state) { + + case dfuERROR: + // Clear an error + set_state(dfuIDLE, OK); + return true; + + default: + // Unexpected request + set_state(dfuERROR, errSTALLEDPKT); + return false; + } +} + +bool dfu_abort(void) +{ + set_state(dfuIDLE, OK); + return true; +} + +/* +void MSC_Handler(void) { + uint32_t msc_irq_reason = MSC->IF; + + if (msc_irq_reason & MSC_IF_WRITE) { + // Write the buffer word to the currently selected address. + // Note that after this is done, the address is incremented by 4. + if (fl_num_words > 0) { + fl_num_words--; + dfu_target_address += 4; + MSC->ADDRB = dfu_target_address; + ftfl_busy_wait(); + MSC->WDATA = dfu_buffer[dfu_buffer_offset++]; + MSC->WRITECMD = MSC_WRITECMD_WRITEONCE; + } + else { + // Move to the IDLE state only if we're out of data to write. + fl_state = flsIDLE; + } + } + + // Clear iterrupts so we don't fire again. + MSC->IFC = MSC_IFC_ERASE | MSC_IFC_WRITE; +} +*/ \ No newline at end of file diff --git a/sw/src/toboot.c b/sw/src/toboot.c new file mode 100644 index 0000000..19b28a2 --- /dev/null +++ b/sw/src/toboot.c @@ -0,0 +1,138 @@ +#include "toboot-api.h" +#include "toboot-internal.h" + +#define XXH_NO_LONG_LONG +#define XXH_FORCE_ALIGN_CHECK 0 +#define XXH_FORCE_NATIVE_FORMAT 0 +#define XXH_PRIVATE_API +#include "xxhash.h" + +static const struct toboot_configuration *current_config = NULL; + +uint32_t tb_first_free_address(void) { + return 131072; + /* + extern uint32_t _eflash; + extern uint32_t _sdtext; + extern uint32_t _edtext; +#define PADDR(x) ((uint32_t)&x) +#define PAGE_SIZE 1024 +#define PAGE_ROUND_UP(x) ( (((uint32_t)(x)) + PAGE_SIZE-1) & (~(PAGE_SIZE-1)) ) + return PAGE_ROUND_UP(PADDR(_eflash) + (PADDR(_edtext) - PADDR(_sdtext))); +#undef PADDR +#undef PAGE_SIZE +#undef PAGE_ROUND_UP +*/ +} + +uint32_t tb_config_hash(const struct toboot_configuration *cfg) { + return XXH32(cfg, sizeof(*cfg) - 4, TOBOOT_HASH_SEED); +} + +void tb_sign_config(struct toboot_configuration *cfg) { + cfg->reserved_hash = tb_config_hash(cfg); +} + +int tb_valid_signature_at_page(uint32_t page) { + const struct toboot_configuration *cfg = (const struct toboot_configuration *)((page * 1024) + 0x94); + if (cfg->magic != TOBOOT_V2_MAGIC) + return -1; + + uint32_t calc_hash = tb_config_hash(cfg); + if (calc_hash != cfg->reserved_hash) + return -2; + + return 0; +} + +uint32_t tb_first_free_sector(void) { + return tb_first_free_address() / 1024; +} + +const struct toboot_configuration *tb_get_config(void) { + uint32_t __app_start__ = 131072; + + // When examining every application in flash, find the newest program + // with the highest generation counter. + uint32_t newest_generation = 0; + + // Fake toboot config, for v1 and v0 programs. + static struct toboot_configuration fake_config; + + if (current_config) + return current_config; + + // Look for a V2 header + uint32_t page; + for (page = 1; page < 65536/1024; page++) { + if (!tb_valid_signature_at_page(page)) { + const struct toboot_configuration *test_cfg = (const struct toboot_configuration *)((page * 1024) + 0x94); + if (test_cfg->reserved_gen > newest_generation) { + newest_generation = test_cfg->reserved_gen; + current_config = test_cfg; + } + } + } + + if (current_config) + return current_config; + + // No V2 header found, so create one. + + // Fake V2 magic + fake_config.magic = TOBOOT_V2_MAGIC; + + if (((*((uint32_t *)(((uint32_t)&__app_start__) + 0x98))) & TOBOOT_V1_MAGIC_MASK) == TOBOOT_V1_MAGIC) + // Applications that know about Toboot will indicate their block + // offset by placing a magic byte at offset 0x98. + // Ordinarily this would be the address offset for IRQ 22, + // but since there are only 20 IRQs on the EFM32HG, there are three + // 32-bit values that are unused starting at offset 0x94. + // We already use offset 0x94 for "disable boot", so use offset 0x98 + // in the incoming stream to indicate flags for Toboot. + fake_config.start = ((*((uint32_t *)(((uint32_t)&__app_start__) + 0x98))) & TOBOOT_V1_APP_PAGE_MASK) >> TOBOOT_V1_APP_PAGE_SHIFT; + else + // Default to offset 0x4000 + fake_config.start = 16; + + // Leave interrupts enabled (and indicate the header is fake) + fake_config.config = TOBOOT_CONFIG_FLAG_ENABLE_IRQ | TOBOOT_CONFIG_FAKE; + + // Lock out bootloader entry, if the magic value is present + if (((*((uint32_t *)(((uint32_t)&__app_start__) + 0x94))) & TOBOOT_V1_CFG_MAGIC_MASK) == TOBOOT_V1_CFG_MAGIC) + fake_config.lock_entry = TOBOOT_LOCKOUT_MAGIC; + else + fake_config.lock_entry = 0; + + // Don't erase anything in particular + fake_config.erase_mask_lo = 0; + fake_config.erase_mask_hi = 0; + + // Calculate a valid hash + tb_sign_config(&fake_config); + + return &fake_config; +} + +uint32_t tb_generation(const struct toboot_configuration *cfg) { + if (!cfg) + return 0; + return cfg->reserved_gen; +} + +__attribute__ ((used, section(".toboot_configuration"))) struct toboot_configuration toboot_configuration = { + .magic = TOBOOT_V2_MAGIC, + + // The current "generation" flag sits at the same location as the + // old Toboot "Config" flag. By setting "reserved_gen" to this value, + // we can make the V1 bootloader treat V2 images as valid. + .reserved_gen = TOBOOT_V1_APP_MAGIC, + + .start = 0, + .config = 0, + + .lock_entry = 0, + .erase_mask_lo = 0, + .erase_mask_hi = 0, + .reserved_hash = 0, +}; diff --git a/sw/src/usb-desc.c b/sw/src/usb-desc.c new file mode 100644 index 0000000..1ef22d5 --- /dev/null +++ b/sw/src/usb-desc.c @@ -0,0 +1,229 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2013 PJRC.COM, LLC. + * + * 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. + */ + +#include + +// USB Descriptors are binary data which the USB host reads to +// automatically detect a USB device's capabilities. The format +// and meaning of every field is documented in numerous USB +// standards. When working with USB descriptors, despite the +// complexity of the standards and poor writing quality in many +// of those documents, remember descriptors are nothing more +// than constant binary data that tells the USB host what the +// device can do. Computers will load drivers based on this data. +// Those drivers then communicate on the endpoints specified by +// the descriptors. + +// To configure a new combination of interfaces or make minor +// changes to existing configuration (eg, change the name or ID +// numbers), usually you would edit "usb_desc.h". This file +// is meant to be configured by the header, so generally it is +// only edited to add completely new USB interfaces or features. + +// ************************************************************** +// USB Device +// ************************************************************** + +#define LSB(n) ((n) & 255) +#define MSB(n) (((n) >> 8) & 255) + +#define USB_DT_BOS_SIZE 5 +#define USB_DT_BOS 0xf +#define USB_DT_DEVICE_CAPABILITY 0x10 +#define USB_DC_PLATFORM 5 + +struct usb_bos_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumDeviceCaps; +} __attribute__((packed)); + +// USB Device Descriptor. The USB host reads this first, to learn +// what type of device is connected. +static const uint8_t device_descriptor[] = { + 18, // bLength + 1, // bDescriptorType + 0x10, 0x02, // bcdUSB + 0x00, // bDeviceClass + 0x00, // bDeviceSubClass + 0x00, // bDeviceProtocol + EP0_SIZE, // bMaxPacketSize0 + LSB(VENDOR_ID), MSB(VENDOR_ID), // idVendor + LSB(PRODUCT_ID), MSB(PRODUCT_ID), // idProduct + LSB(DEVICE_VER), MSB(DEVICE_VER), // bcdDevice + 1, // iManufacturer + 2, // iProduct + 0, // iSerialNumber + 1 // bNumConfigurations +}; + +// These descriptors must NOT be "const", because the USB DMA +// has trouble accessing flash memory with enough bandwidth +// while the processor is executing from flash. + + +// ************************************************************** +// USB Configuration +// ************************************************************** + +// USB Configuration Descriptor. This huge descriptor tells all +// of the devices capbilities. +static const uint8_t config_descriptor[CONFIG_DESC_SIZE] = { + // configuration descriptor, USB spec 9.6.3, page 264-266, Table 9-10 + 9, // bLength; + 2, // bDescriptorType; + LSB(CONFIG_DESC_SIZE), // wTotalLength + MSB(CONFIG_DESC_SIZE), + NUM_INTERFACE, // bNumInterfaces + 1, // bConfigurationValue + 2, // iConfiguration + 0x80, // bmAttributes + 50, // bMaxPower + + // interface descriptor, DFU Mode (DFU spec Table 4.4) + 9, // bLength + 4, // bDescriptorType + DFU_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 0, // bNumEndpoints + 0xFE, // bInterfaceClass + 0x01, // bInterfaceSubClass + 0x02, // bInterfaceProtocol + 2, // iInterface + + // DFU Functional Descriptor (DFU spec TAble 4.2) + 9, // bLength + 0x21, // bDescriptorType + 0x0D, // bmAttributes + LSB(DFU_DETACH_TIMEOUT), // wDetachTimeOut + MSB(DFU_DETACH_TIMEOUT), + LSB(DFU_TRANSFER_SIZE), // wTransferSize + MSB(DFU_TRANSFER_SIZE), + 0x01,0x01, // bcdDFUVersion +}; + + +// ************************************************************** +// String Descriptors +// ************************************************************** + +// The descriptors above can provide human readable strings, +// referenced by index numbers. These descriptors are the +// actual string data + +static const struct usb_string_descriptor_struct string0 = { + 4, + 3, + {0x0409} +}; + +// Microsoft OS String Descriptor. See: https://github.com/pbatard/libwdi/wiki/WCID-Devices +static const struct usb_string_descriptor_struct usb_string_microsoft = { + 18, 3, + {'M','S','F','T','1','0','0', MSFT_VENDOR_CODE} +}; + +// Microsoft WCID +const uint8_t usb_microsoft_wcid[MSFT_WCID_LEN] = { + MSFT_WCID_LEN, 0, 0, 0, // Length + 0x00, 0x01, // Version + 0x04, 0x00, // Compatibility ID descriptor index + 0x01, // Number of sections + 0, 0, 0, 0, 0, 0, 0, // Reserved (7 bytes) + + 0, // Interface number + 0x01, // Reserved + 'W','I','N','U','S','B',0,0, // Compatible ID + 0,0,0,0,0,0,0,0, // Sub-compatible ID (unused) + 0,0,0,0,0,0, // Reserved +}; + +const struct webusb_url_descriptor landing_url_descriptor = { + .bLength = LANDING_PAGE_DESCRIPTOR_SIZE, + .bDescriptorType = WEBUSB_DT_URL, + .bScheme = WEBUSB_URL_SCHEME_HTTPS, + .URL = LANDING_PAGE_URL +}; + +struct full_bos { + struct usb_bos_descriptor bos; + struct webusb_platform_descriptor webusb; +}; + +static const struct full_bos full_bos = { + .bos = { + .bLength = USB_DT_BOS_SIZE, + .bDescriptorType = USB_DT_BOS, + .wTotalLength = USB_DT_BOS_SIZE + WEBUSB_PLATFORM_DESCRIPTOR_SIZE, + .bNumDeviceCaps = 1, + }, + .webusb = { + .bLength = WEBUSB_PLATFORM_DESCRIPTOR_SIZE, + .bDescriptorType = USB_DT_DEVICE_CAPABILITY, + .bDevCapabilityType = USB_DC_PLATFORM, + .bReserved = 0, + .platformCapabilityUUID = WEBUSB_UUID, + .bcdVersion = 0x0100, + .bVendorCode = WEBUSB_VENDOR_CODE, + .iLandingPage = 1, + }, +}; + +__attribute__((aligned(4))) +static const struct usb_string_descriptor_struct usb_string_manufacturer_name = { + 2 + MANUFACTURER_NAME_LEN, + 3, + MANUFACTURER_NAME +}; + +__attribute__((aligned(4))) +struct usb_string_descriptor_struct usb_string_product_name = { + 2 + PRODUCT_NAME_LEN, + 3, + PRODUCT_NAME +}; + +// ************************************************************** +// Descriptors List +// ************************************************************** + +// This table provides access to all the descriptor data above. + +const usb_descriptor_list_t usb_descriptor_list[] = { + {0x0100, sizeof(device_descriptor), device_descriptor}, + {0x0200, sizeof(config_descriptor), config_descriptor}, + {0x0300, 0, (const uint8_t *)&string0}, + {0x0301, 0, (const uint8_t *)&usb_string_manufacturer_name}, + {0x0302, 0, (const uint8_t *)&usb_string_product_name}, + {0x03EE, 0, (const uint8_t *)&usb_string_microsoft}, + {0x0F00, sizeof(full_bos), (const uint8_t *)&full_bos}, + {0, 0, NULL} +}; diff --git a/sw/src/usb-dev.c b/sw/src/usb-dev.c new file mode 100644 index 0000000..273204c --- /dev/null +++ b/sw/src/usb-dev.c @@ -0,0 +1,245 @@ +#include +#include +#include +#include + +#include + +#include + +static uint8_t reply_buffer[8]; +static uint8_t usb_configuration = 0; +#define USB_MAX_PACKET_SIZE 64 /* For FS device */ +static uint8_t rx_buffer[USB_MAX_PACKET_SIZE]; + +void usb_setup(struct usb_device *dev, const struct usb_setup_request *setup) +{ + const uint8_t *data = NULL; + uint32_t datalen = 0; + const usb_descriptor_list_t *list; + + // printf("%s:%d SETUP packet (%04x) value: %02x index: %02x\n", __FILE__, __LINE__, setup->wRequestAndType, setup->wIndex, setup->wValue); + + switch (setup->wRequestAndType) + { + case 0x0500: // SET_ADDRESS + // TODO: Handle set_daddr + // efm32hg_set_daddr(setup->wValue); + break; + case 0x0900: // SET_CONFIGURATION + usb_configuration = setup->wValue; + break; + case 0x0880: // GET_CONFIGURATION + reply_buffer[0] = usb_configuration; + datalen = 1; + data = reply_buffer; + break; + case 0x0080: // GET_STATUS (device) + reply_buffer[0] = 0; + reply_buffer[1] = 0; + datalen = 2; + data = reply_buffer; + break; + case 0x0082: // GET_STATUS (endpoint) + if (setup->wIndex > 0) + { + printf("get_status (setup->wIndex: %d)\n", setup->wIndex); + usb_err(dev, 0); + return; + } + reply_buffer[0] = 0; + reply_buffer[1] = 0; + + // XXX handle endpoint stall here +// if (USB->DIEP0CTL & USB_DIEP_CTL_STALL) +// reply_buffer[0] = 1; + data = reply_buffer; + datalen = 2; + break; + case 0x0102: // CLEAR_FEATURE (endpoint) + if (setup->wIndex > 0 || setup->wValue != 0) + { + // TODO: do we need to handle IN vs OUT here? + printf("%s:%d clear feature (%d / %d)\n", __FILE__, __LINE__, setup->wIndex, setup->wValue); + usb_err(dev, 0); + return; + } + // XXX: Should we clear the stall bit? + // USB->DIEP0CTL &= ~USB_DIEP_CTL_STALL; + // TODO: do we need to clear the data toggle here? + break; + case 0x0302: // SET_FEATURE (endpoint) + if (setup->wIndex > 0 || setup->wValue != 0) + { + // TODO: do we need to handle IN vs OUT here? + printf("%s:%d clear feature (%d / %d)\n", __FILE__, __LINE__, setup->wIndex, setup->wValue); + usb_err(dev, 0); + return; + } + // XXX: Should we set the stall bit? + // USB->DIEP0CTL |= USB_DIEP_CTL_STALL; + // TODO: do we need to clear the data toggle here? + break; + case 0x0680: // GET_DESCRIPTOR + case 0x0681: + for (list = usb_descriptor_list; 1; list++) + { + if (list->addr == NULL) + break; + if (setup->wValue == list->wValue) + { + data = list->addr; + if ((setup->wValue >> 8) == 3) + { + // for string descriptors, use the descriptor's + // length field, allowing runtime configured + // length. + datalen = *(list->addr); + } + else + { + datalen = list->length; + } + goto send; + } + } + printf("%s:%d couldn't find descriptor (%d / %d)\n", __FILE__, __LINE__, setup->wIndex, setup->wValue); + usb_err(dev, 0); + return; + + case (MSFT_VENDOR_CODE << 8) | 0xC0: // Get Microsoft descriptor + case (MSFT_VENDOR_CODE << 8) | 0xC1: + if (setup->wIndex == 0x0004) + { + // Return WCID descriptor + data = usb_microsoft_wcid; + datalen = MSFT_WCID_LEN; + break; + } + printf("%s:%d couldn't find microsoft descriptor (%d / %d)\n", __FILE__, __LINE__, setup->wIndex, setup->wValue); + usb_err(dev, 0); + return; + + case (WEBUSB_VENDOR_CODE << 8) | 0xC0: // Get WebUSB descriptor + if (setup->wIndex == 0x0002) + { + if (setup->wValue == 0x0001) + { + // Return landing page URL descriptor + data = (uint8_t*)&landing_url_descriptor; + datalen = LANDING_PAGE_DESCRIPTOR_SIZE; + break; + } + } + printf("%s:%d couldn't find webusb descriptor (%d / %d)\n", __FILE__, __LINE__, setup->wIndex, setup->wValue); + usb_err(dev, 0); + return; + + case 0x0121: // DFU_DNLOAD + if (setup->wIndex > 0) + { + printf("%s:%d dfu download descriptor index invalid (%d / %d)\n", __FILE__, __LINE__, setup->wIndex, setup->wValue); + usb_err(dev, 0); + return; + } + // Data comes in the OUT phase. But if it's a zero-length request, handle it now. + if (setup->wLength == 0) + { + if (!dfu_download(setup->wValue, 0, 0, 0, NULL)) + { + usb_err(dev, 0); + return; + } + usb_ack(dev, 0); + return; + } + unsigned int len = setup->wLength; + if (len > sizeof(rx_buffer)) + len = sizeof(rx_buffer); + usb_recv(dev, rx_buffer, len); + return; + + case 0x03a1: // DFU_GETSTATUS + if (setup->wIndex > 0) + { + printf("%s:%d err (%d / %d)\n", __FILE__, __LINE__, setup->wIndex, setup->wValue); + usb_err(dev, 0); + return; + } + if (dfu_getstatus(reply_buffer)) + { + data = reply_buffer; + datalen = 6; + break; + } + else + { + printf("%s:%d err (%d / %d)\n", __FILE__, __LINE__, setup->wIndex, setup->wValue); + usb_err(dev, 0); + return; + } + break; + + case 0x0421: // DFU_CLRSTATUS + if (setup->wIndex > 0) + { + printf("%s:%d err (%d / %d)\n", __FILE__, __LINE__, setup->wIndex, setup->wValue); + usb_err(dev, 0); + return; + } + if (dfu_clrstatus()) + { + break; + } + else + { + printf("%s:%d err (%d / %d)\n", __FILE__, __LINE__, setup->wIndex, setup->wValue); + usb_err(dev, 0); + return; + } + + case 0x05a1: // DFU_GETSTATE + if (setup->wIndex > 0) + { + printf("%s:%d err (%d / %d)\n", __FILE__, __LINE__, setup->wIndex, setup->wValue); + usb_err(dev, 0); + return; + } + reply_buffer[0] = dfu_getstate(); + data = reply_buffer; + datalen = 1; + break; + + case 0x0621: // DFU_ABORT + if (setup->wIndex > 0) + { + printf("%s:%d err (%d / %d)\n", __FILE__, __LINE__, setup->wIndex, setup->wValue); + usb_err(dev, 0); + return; + } + if (dfu_abort()) + { + break; + } + else + { + printf("%s:%d err (%d / %d)\n", __FILE__, __LINE__, setup->wIndex, setup->wValue); + usb_err(dev, 0); + return; + } + + default: + printf("%s:%d unrecognized request type (%04x) value: %02x index: %02x\n", __FILE__, __LINE__, setup->wRequestAndType, setup->wIndex, setup->wValue); + usb_err(dev, 0); + return; + } + +send: + if (data && datalen) { + printf("%s:%d sending %d bytes from %08x\n", __FILE__, __LINE__, datalen, data); + usb_send(dev, 0, data, datalen); + } + else + usb_ack(dev, 0); + return; +}