sw: add missing include files

Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
2019-03-05 12:02:02 +08:00
parent ffd0285613
commit c78e9ab214
11 changed files with 2707 additions and 0 deletions

500
sw/src/dfu.c Normal file
View 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
View 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
View 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
View 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;
}