sw: add missing include files
Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
parent
ffd0285613
commit
c78e9ab214
81
sw/include/dfu.h
Normal file
81
sw/include/dfu.h
Normal file
@ -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 <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
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 */
|
109
sw/include/toboot-api.h
Normal file
109
sw/include/toboot-api.h
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
#ifndef TOBOOT_API_H_
|
||||||
|
#define TOBOOT_API_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/// 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_ */
|
49
sw/include/toboot-internal.h
Normal file
49
sw/include/toboot-internal.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#ifndef TOBOOT_INTERNAL_H_
|
||||||
|
#define TOBOOT_INTERNAL_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/// 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_ */
|
95
sw/include/usb-desc.h
Normal file
95
sw/include/usb-desc.h
Normal file
@ -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 <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <dfu.h>
|
||||||
|
#include <webusb-defs.h>
|
||||||
|
|
||||||
|
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
|
55
sw/include/webusb-defs.h
Normal file
55
sw/include/webusb-defs.h
Normal file
@ -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 <stdint.h>
|
||||||
|
|
||||||
|
#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
|
912
sw/include/xxhash.c
Normal file
912
sw/include/xxhash.c
Normal file
@ -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 <stdlib.h>
|
||||||
|
static void* XXH_malloc(size_t s) { return malloc(s); }
|
||||||
|
static void XXH_free (void* p) { free(p); }
|
||||||
|
/*! and for memcpy() */
|
||||||
|
#include <string.h>
|
||||||
|
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 <stdint.h>
|
||||||
|
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<bEnd) {
|
||||||
|
h32 += (*p) * PRIME32_5;
|
||||||
|
h32 = XXH_rotl32(h32, 11) * PRIME32_1 ;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
h32 ^= h32 >> 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<bEnd) {
|
||||||
|
h32 += (*p) * PRIME32_5;
|
||||||
|
h32 = XXH_rotl32(h32, 11) * PRIME32_1;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
h32 ^= h32 >> 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 <stdint.h>
|
||||||
|
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<bEnd) {
|
||||||
|
h64 ^= (*p) * PRIME64_5;
|
||||||
|
h64 = XXH_rotl64(h64, 11) * PRIME64_1;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
h64 ^= h64 >> 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<bEnd) {
|
||||||
|
h64 ^= (*p) * PRIME64_5;
|
||||||
|
h64 = XXH_rotl64(h64, 11) * PRIME64_1;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
h64 ^= h64 >> 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 */
|
294
sw/include/xxhash.h
Normal file
294
sw/include/xxhash.h
Normal file
@ -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 <stddef.h> /* 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 */
|
500
sw/src/dfu.c
Normal file
500
sw/src/dfu.c
Normal file
@ -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 <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <toboot-api.h>
|
||||||
|
#include <toboot-internal.h>
|
||||||
|
#include <dfu.h>
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
*/
|
138
sw/src/toboot.c
Normal file
138
sw/src/toboot.c
Normal file
@ -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,
|
||||||
|
};
|
229
sw/src/usb-desc.c
Normal file
229
sw/src/usb-desc.c
Normal file
@ -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-desc.h>
|
||||||
|
|
||||||
|
// 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}
|
||||||
|
};
|
245
sw/src/usb-dev.c
Normal file
245
sw/src/usb-dev.c
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <usb.h>
|
||||||
|
#include <dfu.h>
|
||||||
|
|
||||||
|
#include <printf.h>
|
||||||
|
|
||||||
|
#include <usb-desc.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user