forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
455 lines
11 KiB
455 lines
11 KiB
/* |
|
* regnual.c -- Firmware installation for STM32F103 Flash ROM |
|
* |
|
* Copyright (C) 2012, 2013, 2015, 2016, 2017 |
|
* Free Software Initiative of Japan |
|
* Author: NIIBE Yutaka <[email protected]> |
|
* |
|
* This file is a part of Gnuk, a GnuPG USB Token implementation. |
|
* |
|
* Gnuk is free software: you can redistribute it and/or modify it |
|
* under the terms of the GNU General Public License as published by |
|
* the Free Software Foundation, either version 3 of the License, or |
|
* (at your option) any later version. |
|
* |
|
* Gnuk is distributed in the hope that it will be useful, but WITHOUT |
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public |
|
* License for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
* |
|
*/ |
|
|
|
/* |
|
* ReGNUal |
|
*/ |
|
|
|
#include "types.h" |
|
#include "usb_lld.h" |
|
#include "sys.h" |
|
|
|
extern void *memset (void *s, int c, size_t n); |
|
|
|
extern void set_led (int); |
|
extern int flash_write (uint32_t dst_addr, const uint8_t *src, size_t len); |
|
extern int flash_protect (void); |
|
extern void nvic_system_reset (void); |
|
|
|
|
|
#define FLASH_START_ADDR 0x08000000 /* Fixed for all STM32F1. */ |
|
#define FLASH_OFFSET 0x1000 /* First pages are not-writable. */ |
|
#define FLASH_START (FLASH_START_ADDR+FLASH_OFFSET) |
|
#define FLASH_SIZE_REG ((uint16_t *)0x1ffff7e0) |
|
static uint32_t flash_end; |
|
|
|
|
|
#define ENDP0_RXADDR (0x40) |
|
#define ENDP0_TXADDR (0x80) |
|
|
|
/* USB Standard Device Descriptor */ |
|
static const uint8_t regnual_device_desc[] = { |
|
18, /* bLength */ |
|
DEVICE_DESCRIPTOR, /* bDescriptorType */ |
|
0x10, 0x01, /* bcdUSB = 1.1 */ |
|
0xFF, /* bDeviceClass: VENDOR */ |
|
0x00, /* bDeviceSubClass */ |
|
0x00, /* bDeviceProtocol */ |
|
0x40, /* bMaxPacketSize0 */ |
|
#include "../src/usb-vid-pid-ver.c.inc" |
|
1, /* Index of string descriptor describing manufacturer */ |
|
2, /* Index of string descriptor describing product */ |
|
3, /* Index of string descriptor describing the device's serial number */ |
|
0x01 /* bNumConfigurations */ |
|
}; |
|
|
|
#if defined(USB_SELF_POWERED) |
|
#define REGNUAL_FEATURE_INIT 0xC0 /* self powered */ |
|
#else |
|
#define REGNUAL_FEATURE_INIT 0x80 /* bus powered */ |
|
#endif |
|
|
|
static const uint8_t regnual_config_desc[] = { |
|
9, |
|
CONFIG_DESCRIPTOR, /* bDescriptorType: Configuration */ |
|
18, 0, /* wTotalLength: no of returned bytes */ |
|
1, /* bNumInterfaces: single vendor interface */ |
|
0x01, /* bConfigurationValue: Configuration value */ |
|
0x00, /* iConfiguration: None */ |
|
REGNUAL_FEATURE_INIT, /* bmAttributes: bus powered */ |
|
50, /* MaxPower 100 mA */ |
|
|
|
/* Interface Descriptor */ |
|
9, |
|
INTERFACE_DESCRIPTOR, /* bDescriptorType: Interface */ |
|
0, /* bInterfaceNumber: Index of this interface */ |
|
0, /* Alternate setting for this interface */ |
|
0, /* bNumEndpoints: None */ |
|
0xFF, |
|
0, |
|
0, |
|
0, /* string index for interface */ |
|
}; |
|
|
|
static const uint8_t regnual_string_lang_id[] = { |
|
4, /* bLength */ |
|
STRING_DESCRIPTOR, |
|
0x09, 0x04 /* LangID = 0x0409: US-English */ |
|
}; |
|
|
|
#include "../src/usb-strings.c.inc" |
|
|
|
static const uint8_t regnual_string_serial[] = { |
|
8*2+2, |
|
STRING_DESCRIPTOR, |
|
/* FSIJ-0.0 */ |
|
'F', 0, 'S', 0, 'I', 0, 'J', 0, '-', 0, |
|
'0', 0, '.', 0, '0', 0, |
|
}; |
|
|
|
|
|
static void |
|
usb_device_reset (struct usb_dev *dev) |
|
{ |
|
usb_lld_reset (dev, REGNUAL_FEATURE_INIT); |
|
|
|
/* Initialize Endpoint 0 */ |
|
usb_lld_setup_endpoint (ENDP0, EP_CONTROL, 0, ENDP0_RXADDR, ENDP0_TXADDR, |
|
64); |
|
} |
|
|
|
#define USB_REGNUAL_MEMINFO 0 |
|
#define USB_REGNUAL_SEND 1 |
|
#define USB_REGNUAL_RESULT 2 |
|
#define USB_REGNUAL_FLASH 3 |
|
#define USB_REGNUAL_PROTECT 4 |
|
#define USB_REGNUAL_FINISH 5 |
|
|
|
static uint32_t mem[256/4]; |
|
static uint32_t result; |
|
|
|
|
|
static uint32_t rbit (uint32_t v) |
|
{ |
|
uint32_t r; |
|
|
|
asm ("rbit %0, %1" : "=r" (r) : "r" (v)); |
|
return r; |
|
} |
|
|
|
static uint32_t fetch (int i) |
|
{ |
|
uint32_t v; |
|
|
|
v = mem[i]; |
|
return rbit (v); |
|
} |
|
|
|
struct CRC { |
|
__IO uint32_t DR; |
|
__IO uint8_t IDR; |
|
uint8_t RESERVED0; |
|
uint16_t RESERVED1; |
|
__IO uint32_t CR; |
|
}; |
|
|
|
#define CRC_CR_RESET 0x01 |
|
static uint32_t calc_crc32 (void) |
|
{ |
|
struct CRC *CRC = (struct CRC *)0x40023000; |
|
int i; |
|
|
|
CRC->CR = CRC_CR_RESET; |
|
|
|
for (i = 0; i < 256/4; i++) |
|
CRC->DR = fetch (i); |
|
|
|
return rbit (CRC->DR); |
|
} |
|
|
|
|
|
static void |
|
usb_ctrl_write_finish (struct usb_dev *dev) |
|
{ |
|
struct device_req *arg = &dev->dev_req; |
|
uint8_t type_rcp = arg->type & (REQUEST_TYPE|RECIPIENT); |
|
|
|
if (type_rcp == (VENDOR_REQUEST | DEVICE_RECIPIENT) |
|
&& USB_SETUP_SET (arg->type)) |
|
{ |
|
if (arg->request == USB_REGNUAL_SEND && arg->value == 0) |
|
result = calc_crc32 (); |
|
else if (arg->request == USB_REGNUAL_FLASH) |
|
{ |
|
uint32_t dst_addr = (0x08000000 + arg->value * 0x100); |
|
|
|
result = flash_write (dst_addr, (const uint8_t *)mem, 256); |
|
} |
|
else if (arg->request == USB_REGNUAL_PROTECT && arg->value == 0) |
|
result = flash_protect (); |
|
else if (arg->request == USB_REGNUAL_FINISH && arg->value == 0) |
|
nvic_system_reset (); |
|
} |
|
} |
|
|
|
static int |
|
usb_setup (struct usb_dev *dev) |
|
{ |
|
struct device_req *arg = &dev->dev_req; |
|
uint8_t type_rcp = arg->type & (REQUEST_TYPE|RECIPIENT); |
|
|
|
if (type_rcp == (VENDOR_REQUEST | DEVICE_RECIPIENT)) |
|
{ |
|
if (USB_SETUP_GET (arg->type)) |
|
{ |
|
if (arg->request == USB_REGNUAL_MEMINFO) |
|
{ |
|
const uint8_t *mem_info[2]; |
|
|
|
mem_info[0] = (const uint8_t *)FLASH_START; |
|
mem_info[1] = (const uint8_t *)flash_end; |
|
return usb_lld_ctrl_send (dev, mem_info, sizeof (mem_info)); |
|
} |
|
else if (arg->request == USB_REGNUAL_RESULT) |
|
return usb_lld_ctrl_send (dev, &result, sizeof (uint32_t)); |
|
} |
|
else /* SETUP_SET */ |
|
{ |
|
if (arg->request == USB_REGNUAL_SEND) |
|
{ |
|
if (arg->value != 0 || arg->index + arg->len > 256) |
|
return -1; |
|
|
|
if (arg->index + arg->len < 256) |
|
memset ((uint8_t *)mem + arg->index + arg->len, 0xff, |
|
256 - (arg->index + arg->len)); |
|
|
|
return usb_lld_ctrl_recv (dev, mem + arg->index, arg->len); |
|
} |
|
else if (arg->request == USB_REGNUAL_FLASH && arg->len == 0 |
|
&& arg->index == 0) |
|
{ |
|
uint32_t dst_addr = (0x08000000 + arg->value * 0x100); |
|
|
|
if (dst_addr + 256 <= flash_end) |
|
return usb_lld_ctrl_ack (dev); |
|
} |
|
else if (arg->request == USB_REGNUAL_PROTECT && arg->len == 0 |
|
&& arg->value == 0 && arg->index == 0) |
|
return usb_lld_ctrl_ack (dev); |
|
else if (arg->request == USB_REGNUAL_FINISH && arg->len == 0 |
|
&& arg->value == 0 && arg->index == 0) |
|
return usb_lld_ctrl_ack (dev); |
|
} |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
static int |
|
usb_get_descriptor (struct usb_dev *dev) |
|
{ |
|
struct device_req *arg = &dev->dev_req; |
|
uint8_t rcp = arg->type & RECIPIENT; |
|
uint8_t desc_type = (arg->value >> 8); |
|
uint8_t desc_index = (arg->value & 0xff); |
|
|
|
if (rcp != DEVICE_RECIPIENT) |
|
return -1; |
|
|
|
if (desc_type == DEVICE_DESCRIPTOR) |
|
return usb_lld_ctrl_send (dev, regnual_device_desc, |
|
sizeof (regnual_device_desc)); |
|
else if (desc_type == CONFIG_DESCRIPTOR) |
|
return usb_lld_ctrl_send (dev, regnual_config_desc, |
|
sizeof (regnual_config_desc)); |
|
else if (desc_type == STRING_DESCRIPTOR) |
|
{ |
|
const uint8_t *str; |
|
int size; |
|
|
|
switch (desc_index) |
|
{ |
|
case 0: |
|
str = regnual_string_lang_id; |
|
size = sizeof (regnual_string_lang_id); |
|
break; |
|
case 1: |
|
str = gnuk_string_vendor; |
|
size = sizeof (gnuk_string_vendor); |
|
break; |
|
case 2: |
|
str = gnuk_string_product; |
|
size = sizeof (gnuk_string_product); |
|
break; |
|
case 3: |
|
str = regnual_string_serial; |
|
size = sizeof (regnual_string_serial); |
|
break; |
|
default: |
|
return -1; |
|
} |
|
|
|
return usb_lld_ctrl_send (dev, str, size); |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
static int |
|
usb_set_configuration (struct usb_dev *dev) |
|
{ |
|
uint8_t current_conf; |
|
|
|
current_conf = usb_lld_current_configuration (dev); |
|
if (current_conf == 0) |
|
{ |
|
if (dev->dev_req.value != 1) |
|
return -1; |
|
|
|
usb_lld_set_configuration (dev, 1); |
|
} |
|
else if (current_conf != dev->dev_req.value) |
|
{ |
|
if (dev->dev_req.value != 0) |
|
return -1; |
|
|
|
usb_lld_set_configuration (dev, 0); |
|
} |
|
|
|
/* Do nothing when current_conf == value */ |
|
return usb_lld_ctrl_ack (dev); |
|
} |
|
|
|
|
|
static void wait (int count) |
|
{ |
|
int i; |
|
|
|
for (i = 0; i < count; i++) |
|
asm volatile ("" : : "r" (i) : "memory"); |
|
} |
|
|
|
#define WAIT 2400000 |
|
|
|
/* NVIC: Nested Vectored Interrupt Controller. */ |
|
struct NVIC { |
|
volatile uint32_t ISER[8]; |
|
uint32_t unused1[24]; |
|
volatile uint32_t ICER[8]; |
|
uint32_t unused2[24]; |
|
volatile uint32_t ISPR[8]; |
|
uint32_t unused3[24]; |
|
volatile uint32_t ICPR[8]; |
|
uint32_t unused4[24]; |
|
volatile uint32_t IABR[8]; |
|
uint32_t unused5[56]; |
|
volatile uint32_t IPR[60]; |
|
}; |
|
static struct NVIC *const NVIC = (struct NVIC *const)0xE000E100; |
|
#define NVIC_ISER(n) (NVIC->ISER[n >> 5]) |
|
|
|
static void nvic_enable_intr (uint8_t irq_num) |
|
{ |
|
NVIC_ISER (irq_num) = 1 << (irq_num & 0x1f); |
|
} |
|
|
|
#define USB_LP_CAN1_RX0_IRQn 20 |
|
static struct usb_dev dev; |
|
|
|
int |
|
main (int argc, char *argv[]) |
|
{ |
|
(void)argc; (void)argv; |
|
|
|
set_led (0); |
|
|
|
#if defined(STM32F103_OVERRIDE_FLASH_SIZE_KB) |
|
flash_end = FLASH_START_ADDR + STM32F103_OVERRIDE_FLASH_SIZE_KB*1024; |
|
#else |
|
flash_end = FLASH_START_ADDR + (*FLASH_SIZE_REG)*1024; |
|
#endif |
|
|
|
/* |
|
* NVIC interrupt priority was set by Gnuk. |
|
* USB interrupt is disabled by NVIC setting. |
|
* We enable the interrupt again by nvic_enable_intr. |
|
*/ |
|
usb_lld_init (&dev, REGNUAL_FEATURE_INIT); |
|
nvic_enable_intr (USB_LP_CAN1_RX0_IRQn); |
|
|
|
while (1) |
|
{ |
|
set_led (1); |
|
wait (WAIT); |
|
set_led (0); |
|
wait (WAIT); |
|
} |
|
} |
|
|
|
void |
|
usb_interrupt_handler (void) |
|
{ |
|
uint8_t ep_num; |
|
int e; |
|
|
|
e = usb_lld_event_handler (&dev); |
|
ep_num = USB_EVENT_ENDP (e); |
|
|
|
if (ep_num == 0) |
|
switch (USB_EVENT_ID (e)) |
|
{ |
|
case USB_EVENT_DEVICE_RESET: |
|
usb_device_reset (&dev); |
|
break; |
|
|
|
case USB_EVENT_DEVICE_ADDRESSED: |
|
break; |
|
|
|
case USB_EVENT_GET_DESCRIPTOR: |
|
if (usb_get_descriptor (&dev) < 0) |
|
usb_lld_ctrl_error (&dev); |
|
break; |
|
|
|
case USB_EVENT_SET_CONFIGURATION: |
|
if (usb_set_configuration (&dev) < 0) |
|
usb_lld_ctrl_error (&dev); |
|
break; |
|
|
|
case USB_EVENT_SET_INTERFACE: |
|
usb_lld_ctrl_error (&dev); |
|
break; |
|
|
|
case USB_EVENT_CTRL_REQUEST: |
|
/* Device specific device request. */ |
|
if (usb_setup (&dev) < 0) |
|
usb_lld_ctrl_error (&dev); |
|
break; |
|
|
|
case USB_EVENT_GET_STATUS_INTERFACE: |
|
usb_lld_ctrl_error (&dev); |
|
break; |
|
|
|
case USB_EVENT_GET_INTERFACE: |
|
usb_lld_ctrl_error (&dev); |
|
break; |
|
|
|
case USB_EVENT_SET_FEATURE_DEVICE: |
|
case USB_EVENT_SET_FEATURE_ENDPOINT: |
|
case USB_EVENT_CLEAR_FEATURE_DEVICE: |
|
case USB_EVENT_CLEAR_FEATURE_ENDPOINT: |
|
usb_lld_ctrl_ack (&dev); |
|
break; |
|
|
|
case USB_EVENT_CTRL_WRITE_FINISH: |
|
/* Control WRITE transfer finished. */ |
|
usb_ctrl_write_finish (&dev); |
|
break; |
|
|
|
case USB_EVENT_OK: |
|
case USB_EVENT_DEVICE_SUSPEND: |
|
default: |
|
break; |
|
} |
|
}
|
|
|