Add 'fw/' from commit '5b94dee9cf'

git-subtree-dir: fw
git-subtree-mainline: 6eddc61626
git-subtree-split: 5b94dee9cf
This commit is contained in:
jaseg 2021-03-02 19:27:52 +01:00
commit b328ef6059
63 changed files with 13005 additions and 0 deletions

69
fw/src/CMakeLists.txt Normal file
View file

@ -0,0 +1,69 @@
set (inc ${CMAKE_SOURCE_DIR}/include)
add_library (usbhost
${USART_HELPERS}
${inc}/usbh_core.h
${inc}/usbh_driver_ac_midi.h
${inc}/usbh_driver_gp_xbox.h
${inc}/usbh_driver_hid.h
${inc}/usbh_driver_hub.h
${inc}/usbh_lld_stm32f4.h
${inc}/driver/usbh_device_driver.h
${inc}/usbh_config.h
usbh_core.c
usbh_driver_ac_midi.c
usbh_driver_ac_midi_private.h
usbh_driver_gp_xbox.c
usbh_driver_hid.c
usbh_driver_hub.c
usbh_driver_hub_private.h
usbh_lld_stm32f4.c
usart_helpers.c
tinyprintf.c
cobs.c
noise.c
packet_interface.c
words.c
hid_keycodes.c
)
add_subdirectory (crypto)
add_definitions (
-DBLAKE2S_USE_VECTOR_MATH=0
)
target_link_libraries (usbhost
noise
${LIBOPENCM3_LIB}
)
add_executable (demo
rand_stm32.c
demo.c
)
target_link_libraries (demo
usbhost
noise
)
add_custom_command (TARGET demo
POST_BUILD
COMMAND ${CMAKE_OBJCOPY} -Oihex $<TARGET_FILE:demo> ${CMAKE_BINARY_DIR}/demo.hex
COMMENT "Generating output files: ${CMAKE_BINARY_DIR}/demo.hex"
)
add_custom_command (TARGET demo
POST_BUILD
COMMAND ${CMAKE_SIZE} $<TARGET_FILE:demo>
COMMENT "Calculating size of the binary"
)
add_custom_command (TARGET usbhost
POST_BUILD
COMMENT "Calculating size of the library"
COMMAND ${CMAKE_SIZE} $<TARGET_FILE:usbhost>
)

390
fw/src/cobs.c Normal file
View file

@ -0,0 +1,390 @@
#include <cobs.h>
/*@ requires \valid(dst + (0..dstlen-1));
@ requires \valid_read(src + (0..srclen-1));
@ requires \separated(dst + (0..dstlen-1), src + (0..srclen-1));
@
@ behavior valid:
@ assumes 0 <= srclen <= 254;
@ assumes 0 <= dstlen <= 65535;
@ assumes dstlen >= srclen+2;
@ assigns dst[0..srclen+1];
@ ensures \forall integer i; (0 <= i < srclen && \old(src[i]) != 0) ==> dst[i+1] == src[i];
@ ensures \result == srclen+2;
@ ensures \forall integer i; 0 <= i <= srclen ==> dst[i] != 0;
@ ensures dst[srclen+1] == 0;
@
@ behavior invalid:
@ assumes srclen < 0 || srclen > 254
@ || dstlen < 0 || dstlen > 65535
@ || dstlen < srclen+2;
@ assigns \nothing;
@ ensures \result == -1;
@
@ complete behaviors;
@ disjoint behaviors;
@*/
ssize_t cobs_encode(char *dst, size_t dstlen, char *src, size_t srclen) {
if (dstlen > 65535 || srclen > 254)
return -1;
//@ assert 0 <= dstlen <= 65535 && 0 <= srclen <= 254;
if (dstlen < srclen+2)
return -1;
//@ assert 0 <= srclen < srclen+2 <= dstlen;
size_t p = 0;
/*@ loop invariant 0 <= p <= srclen+1;
@ loop invariant \forall integer i; 0 <= i < p ==> dst[i] != 0;
@ loop invariant \forall integer i; 0 < i < p ==> (src[i-1] != 0 ==> dst[i] == src[i-1]);
@ loop assigns p, dst[0..srclen+1];
@ loop variant srclen-p+1;
@*/
while (p <= srclen) {
char val;
if (p != 0 && src[p-1] != 0) {
val = src[p-1];
} else {
size_t q = p;
/*@ loop invariant 0 <= p <= q <= srclen;
@ loop invariant \forall integer i; p <= i < q ==> src[i] != 0;
@ loop assigns q;
@ loop variant srclen-q;
@*/
while (q < srclen && src[q] != 0)
q++;
//@ assert q == srclen || src[q] == 0;
//@ assert q <= srclen <= 254;
val = (char)q-p+1;
//@ assert val != 0;
}
dst[p] = val;
p++;
}
dst[p] = 0;
//@ assert p == srclen+1;
return srclen+2;
}
/*@ requires \valid_read(src + (0..srclen-1));
@*/
#ifndef VERIFICATION
int cobs_encode_incremental(void *f, int (*output)(void *f, unsigned char c), unsigned char *src, size_t srclen) {
#else
int fc_verification_cobs_encode_incremental(unsigned char *src, size_t srclen) {
#endif
//@ ghost unsigned char output_buf[1000000];
if (srclen > 254)
return -1;
//@ assert 0 <= srclen <= 254;
size_t p = 0;
/*@ loop invariant 0 <= p <= srclen+1;
@ loop invariant \forall integer i; 0 <= i < p ==> output_buf[i] != 0;
@ loop assigns p, output_buf[0..srclen+1];
@ loop variant srclen-p+1;
@*/
while (p <= srclen) {
unsigned char val;
if (p != 0 && src[p-1] != 0) {
val = src[p-1];
} else {
size_t q = p;
/*@ loop invariant 0 <= p <= q <= srclen;
@ loop invariant \forall integer i; p <= i < q ==> src[i] != 0;
@ loop assigns q;
@ loop variant srclen-q;
@*/
while (q < srclen && src[q] != 0)
q++;
val = (unsigned char)q-p+1;
}
//@ ghost output_buf[p] = val;
#ifndef VERIFICATION
int rv = output(f, val);
if (rv)
return rv;
#endif
p++;
}
//@ assert frame_size: p == srclen+1;
//@ assert synchronization_robustness: \forall integer i; 0 <= i < p ==> output_buf[i] != 0;
#ifndef VERIFICATION
int rv = output(f, 0);
if (rv)
return rv;
#endif
return 0;
}
/*@ requires \valid(dst + (0..dstlen-1));
@ requires \valid_read(src + (0..srclen-1));
@ requires \separated(dst + (0..dstlen-1), src + (0..srclen-1));
@
@ behavior maybe_valid_frame:
@ assumes 1 <= srclen <= dstlen <= 65535;
@ assumes \exists integer j; j > 0 && \forall integer i; 0 <= i < j ==> src[i] != 0;
@ assumes \exists integer i; 0 <= i < srclen && src[i] == 0;
@ assigns dst[0..dstlen-1];
@ ensures \result >= 0 || \result == -3;
@ ensures \result >= 0 ==> src[\result+1] == 0;
@ ensures \result >= 0 ==> (\forall integer i; 0 <= i < \result ==> src[i] != 0);
@
@ behavior invalid_frame:
@ assumes 1 <= srclen <= dstlen <= 65535;
@ assumes src[0] == 0 || \forall integer i; 0 <= i < srclen ==> src[i] != 0;
@ assigns dst[0..dstlen-1];
@ ensures \result == -2;
@
@ behavior invalid_buffers:
@ assumes dstlen < 0 || dstlen > 65535
@ || srclen < 1 || srclen > 65535
@ || dstlen < srclen;
@ assigns \nothing;
@ ensures \result == -1;
@
@ complete behaviors;
@ disjoint behaviors;
@*/
ssize_t cobs_decode(char *dst, size_t dstlen, char *src, size_t srclen) {
if (dstlen > 65535 || srclen > 65535)
return -1;
if (srclen < 1)
return -1;
if (dstlen < srclen)
return -1;
size_t p = 1;
size_t c = (unsigned char)src[0];
//@ assert 0 <= c < 256;
//@ assert 0 <= c;
//@ assert c < 256;
if (c == 0)
return -2; /* invalid framing. An empty frame would be [...] 00 01 00, not [...] 00 00 */
//@ assert c >= 0;
//@ assert c != 0;
//@ assert c <= 257;
//@ assert c > 0;
//@ assert c >= 0 && c != 0 ==> c > 0;
/*@ //loop invariant \forall integer i; 0 <= i <= p ==> (i == srclen || src[i] != 0);
@ loop invariant \forall integer i; 1 <= i < p ==> src[i] != 0;
@ loop invariant c > 0;
@ loop invariant 1 <= p <= srclen <= dstlen <= 65535;
@ loop invariant \separated(dst + (0..dstlen-1), src + (0..srclen-1));
@ loop invariant \valid_read(src + (0..srclen-1));
@ loop invariant \forall integer i; 1 <= i <= srclen ==> \valid(dst + i - 1);
@ loop assigns dst[0..dstlen-1], p, c;
@ loop variant srclen-p;
@*/
while (p < srclen && src[p]) {
char val;
c--;
//@ assert src[p] != 0;
if (c == 0) {
c = (unsigned char)src[p];
val = 0;
} else {
val = src[p];
}
//@ assert 0 <= p-1 <= dstlen-1;
dst[p-1] = val;
p++;
}
if (p == srclen)
return -2; /* Invalid framing. The terminating null byte should always be present in the input buffer. */
if (c != 1)
return -3; /* Invalid framing. The skip counter does not hit the end of the frame. */
//@ assert 0 < p <= srclen <= 65535;
//@ assert src[p] == 0;
//@ assert \forall integer i; 1 <= i < p ==> src[i] != 0;
return p-1;
}
void cobs_decode_incremental_initialize(struct cobs_decode_state *state) {
state->p = 0;
state->c = 0;
}
/*@ requires separation: \separated(state, dst + (0..dstlen-1));
requires state_valid: \valid(state);
requires dst_valid: \valid(dst + (0..dstlen-1));
requires dstlen_bounds: 0 < dstlen <= INT_MAX;
requires p_valid: 0 <= state->p <= dstlen+1;
requires c_valid: state->p != 0 ==> 1 <= state->c <= 255;
// Sanity properties
ensures c_valid: state->p != 0 ==> 1 <= state->c <= 255;
ensures buffer_bounds: 0 <= state->p <= dstlen+1;
ensures return_value: 0 <= \result <= dstlen || \result \in {-1, -2, -3, -4};
ensures reset_on_error: \result < -1 ==> state->p == 0 && state->c == 0;
// Synchronization properties
ensures self_synchronization: src == 0 ==> \result >= 0 || \result \in {-2, -3};
ensures synchronization_robustness: src != 0 ==> \result \in {-1, -4};
// Basic functional sanity
ensures legal_advance: \result != -1 <==> state->p \in {0, \old(state->p)};
ensures incremental_advance: \result == -1 <==> state->p == \old(state->p) + 1;
ensures nonzero_unmodified: \result == -1 && state->p > 1 ==> dst[state->p-2] \in {0, src};
assigns *state, dst[0..dstlen-1];
behavior eof_bad_empty_frame:
assumes state->p == 0 && src == 0;
ensures \result == -3;
assigns *state;
behavior eof_maybe_good_frame:
assumes state->p != 0 && src == 0;
ensures \result >= 0 || \result == -2;
assigns *state;
behavior decoding_no_overflow:
assumes src != 0 && state->p <= dstlen;
ensures \result == -1;
assigns *state, dst[0..dstlen-1];
behavior decoding_overflow:
assumes src != 0 && state->p > dstlen;
ensures \result == -4;
assigns *state;
complete behaviors;
disjoint behaviors;
@*/
int cobs_decode_incremental(struct cobs_decode_state *state, unsigned char *dst, size_t dstlen, unsigned char src) {
if (state->p == 0) {
if (src == 0) {
/* invalid framing. An empty frame would be [...] 00 01 00, not [...] 00 00 */
cobs_decode_incremental_initialize(state);
return -3;
}
state->c = src;
state->p++;
return -1;
}
if (!src) {
if (state->c != 1) {
/* invalid framing. The skip counter does not hit the end of the frame. */
cobs_decode_incremental_initialize(state);
return -2;
}
size_t rv = state->p-1;
cobs_decode_incremental_initialize(state);
return rv;
}
unsigned char val;
state->c--;
if (state->c == 0) {
state->c = src;
val = 0;
} else {
val = src;
}
size_t pos = state->p-1;
if (pos >= dstlen) {
cobs_decode_incremental_initialize(state);
return -4; /* output buffer too small */
}
dst[pos] = val;
state->p++;
return -1;
}
/*@ requires \valid_read(buf + (0..len-1));
assigns \nothing;
@*/
void handle_packet(unsigned char *buf, size_t len);
/*@ requires \valid(dst + (0..dstlen-1));
requires \valid_read(src + (0..srclen-1));
requires 0 < dstlen <= 65535;
assigns dst[0..dstlen-1];
@*/
void cobs_decode_test(unsigned char *src, size_t srclen, unsigned char *dst, size_t dstlen) {
struct cobs_decode_state state;
cobs_decode_incremental_initialize(&state);
//@ assert state.p == 0;
//@ assert state.c == 0;
//@ assert state.p != 0 ==> 1 <= state.c <= 255;
/*@ loop invariant \separated(&state, dst + (0..dstlen-1), &i);
loop invariant \valid(&state);
loop invariant \valid(dst + (0..dstlen-1));
loop invariant 0 < dstlen <= 65535;
loop invariant 0 <= state.p <= dstlen+1;
loop invariant state.p != 0 ==> 1 <= state.c <= 255;
loop assigns dst[0..dstlen-1], state, i;
loop variant srclen-i;
@*/
for (size_t i=0; i<srclen; i++) {
int rv = cobs_decode_incremental(&state, dst, dstlen, src[i]);
if (rv >= 0)
handle_packet(dst, rv);
if (rv == -1)
continue;
if (rv < -1)
continue;
}
}
#ifdef VALIDATION
/*@
@ requires 0 <= d < 256;
@ assigns \nothing;
@*/
size_t test(char foo, unsigned int d) {
unsigned int c = (unsigned char)foo;
if (c != 0) {
//@ assert c < 256;
//@ assert c >= 0;
//@ assert c != 0;
//@ assert c > 0;
}
if (d != 0) {
//@ assert d >= 0;
//@ assert d != 0;
//@ assert d > 0;
}
return c + d;
}
#include <__fc_builtin.h>
void main(void) {
char inbuf[254];
char cobsbuf[256];
char outbuf[256];
size_t range = Frama_C_interval(0, sizeof(inbuf));
Frama_C_make_unknown((char *)inbuf, range);
cobs_encode(cobsbuf, sizeof(cobsbuf), inbuf, sizeof(inbuf));
cobs_decode(outbuf, sizeof(outbuf), cobsbuf, sizeof(cobsbuf));
//@ assert \forall integer i; 0 <= i < sizeof(inbuf) ==> outbuf[i] == inbuf[i];
}
#endif//VALIDATION

View file

@ -0,0 +1,93 @@
include_directories (
noise-c/include
noise-c/include/noise/keys
noise-c/src
noise-c/src/crypto/goldilocks/include
noise-c/src/crypto/goldilocks/src/include
noise-c/src/crypto/goldilocks/src/p448/arch_arm_32
noise-c/src/crypto/goldilocks/src/p448
noise-c/src/protocol
)
add_library (noise
noise-c/src/protocol/util.c
noise-c/src/protocol/patterns.c
noise-c/src/protocol/signstate.c
noise-c/src/protocol/randstate.c
noise-c/src/protocol/symmetricstate.c
noise-c/src/protocol/internal.c
noise-c/src/protocol/names.c
noise-c/src/protocol/hashstate.c
noise-c/src/protocol/errors.c
noise-c/src/protocol/cipherstate.c
noise-c/src/protocol/handshakestate.c
noise-c/src/protocol/dhstate.c
noise-c/src/keys/certificate.c
noise-c/src/keys/loader.c
noise-c/src/crypto/sha2/sha256.c
noise-c/src/crypto/sha2/sha512.c
noise-c/src/crypto/ghash/ghash.c
noise-c/src/crypto/ed25519/ed25519.c
noise-c/src/crypto/blake2/blake2s.c
noise-c/src/crypto/blake2/blake2b.c
noise-c/src/crypto/chacha/chacha.c
noise-c/src/crypto/goldilocks/src/ec_point.c
noise-c/src/crypto/goldilocks/src/sha512.c
noise-c/src/crypto/goldilocks/src/p448/arch_32/p448.c
noise-c/src/crypto/goldilocks/src/p448/f_arithmetic.c
noise-c/src/crypto/goldilocks/src/p448/arch_arm_32/p448.c
noise-c/src/crypto/goldilocks/src/p448/magic.c
noise-c/src/crypto/goldilocks/src/barrett_field.c
noise-c/src/crypto/goldilocks/src/goldilocks.c
noise-c/src/crypto/goldilocks/src/arithmetic.c
noise-c/src/crypto/goldilocks/src/crandom.c
noise-c/src/crypto/goldilocks/src/scalarmul.c
noise-c/src/crypto/newhope/poly.c
noise-c/src/crypto/newhope/randombytes.c
noise-c/src/crypto/newhope/reduce.c
noise-c/src/crypto/newhope/ntt.c
noise-c/src/crypto/newhope/crypto_stream_chacha20.c
noise-c/src/crypto/newhope/error_correction.c
noise-c/src/crypto/newhope/batcher.c
noise-c/src/crypto/newhope/fips202.c
noise-c/src/crypto/newhope/newhope.c
noise-c/src/crypto/newhope/precomp.c
noise-c/src/crypto/aes/rijndael-alg-fst.c
noise-c/src/crypto/curve448/curve448.c
noise-c/src/crypto/donna/poly1305-donna.c
noise-c/src/crypto/donna/curve25519-donna.c
noise-c/src/protobufs/protobufs.c
noise-c/src/backend/ref/sign-ed25519.c
noise-c/src/backend/ref/hash-blake2b.c
noise-c/src/backend/ref/hash-sha512.c
noise-c/src/backend/ref/hash-sha256.c
noise-c/src/backend/ref/cipher-aesgcm.c
noise-c/src/backend/ref/cipher-chachapoly.c
noise-c/src/backend/ref/dh-curve25519.c
noise-c/src/backend/ref/dh-newhope.c
noise-c/src/backend/ref/dh-curve448.c
noise-c/src/backend/ref/hash-blake2s.c
)
add_definitions (
-DUSE_LIBSODIUM=0
-DUSE_SODIUM=0
-DHAVE_PTHREAD=0
-DUSE_OPENSSL=0
-D__WORDSIZE=32
-D__BIG_ENDIAN=4321
-D__LITTLE_ENDIAN=1234
-D__BYTE_ORDER=__LITTLE_ENDIAN
-DED25519_CUSTOMRANDOM=1
-DED25519_CUSTOMHASH=1
-DED25519_REFHASH=1
-DBLAKE2S_USE_VECTOR_MATH=0
-DEXPERIMENT_CRANDOM_CUTOFF_BYTES=0
-D__clang__=0
)
set (CMAKE_C_FLAGS
"${CMAKE_C_FLAGS} -Wno-implicit-fallthrough -Wno-shadow -Wno-unused-parameter"
)

684
fw/src/demo.c Normal file
View file

@ -0,0 +1,684 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2015 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/stm32/timer.h>
#include <libopencm3/stm32/otg_hs.h>
#include <libopencm3/stm32/otg_fs.h>
#include <libopencm3/stm32/pwr.h>
#include <libopencm3/stm32/dma.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencmsis/core_cm3.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "usart_helpers.h"
#include "usbh_core.h"
#include "usbh_lld_stm32f4.h"
#include "usbh_driver_hid.h"
#include "usbh_driver_hub.h"
#include "rand_stm32.h"
#include "packet_interface.h"
#include "noise.h"
#include "hid_keycodes.h"
#include "words.h"
#include "tracing.h"
#include "crypto/noise-c/src/protocol/internal.h"
#ifndef USE_STM32F4_USBH_DRIVER_FS
#error The full-speed USB driver must be enabled with USE_STM32F4_USBH_DRIVER_FS in usbh_config.h!
#endif
#ifndef MAX_FAILED_HANDSHAKES
#define MAX_FAILED_HANDSHAKES 5
#endif
static struct NoiseState noise_state;
static struct {
union {
struct {
uint8_t local_key[CURVE25519_KEY_LEN];
uint8_t remote_key_reference[BLAKE2S_HASH_SIZE];
};
uint32_t all_keys[0];
} keys;
struct {
uint8_t identity_key_valid;
uint8_t scrub_backup;
uint8_t scrubber_armed;
uint32_t old_scrub_pattern;
uint32_t new_scrub_pattern;
int scrub_idx_read;
int scrub_idx_done;
} mgmt __attribute__((aligned(4)));
} keystore __attribute__((section(".backup_sram"))) = {0};
void _fini(void);
static inline void delay(uint32_t n) {
for (volatile uint32_t i = 0; i < 1490*n; i++);
}
/* Set STM32 to 168 MHz. */
static void clock_setup(void) {
rcc_clock_setup_hse_3v3(&hse_8mhz_3v3[CLOCK_3V3_168MHZ]);
rcc_periph_clock_enable(RCC_GPIOA);
rcc_periph_clock_enable(RCC_GPIOB);
rcc_periph_clock_enable(RCC_GPIOD);
rcc_periph_clock_enable(RCC_GPIOE);
rcc_periph_clock_enable(RCC_USART1);
rcc_periph_clock_enable(RCC_USART2);
rcc_periph_clock_enable(RCC_OTGFS);
rcc_periph_clock_enable(RCC_TIM6);
rcc_periph_clock_enable(RCC_DMA2);
rcc_periph_clock_enable(RCC_DMA1);
rcc_periph_clock_enable(RCC_PWR);
rcc_periph_clock_enable(RCC_BKPSRAM);
rcc_periph_clock_enable(RCC_RNG);
}
void arm_key_scrubber() {
keystore.mgmt.scrubber_armed = 1;
}
static void finish_scrub(int start_index, uint32_t pattern);
static void finish_interrupted_scrub(void);
void disarm_key_scrubber() {
keystore.mgmt.scrubber_armed = 0;
keystore.mgmt.old_scrub_pattern = keystore.mgmt.new_scrub_pattern;
keystore.mgmt.new_scrub_pattern = 0x00000000;
finish_scrub(0, keystore.mgmt.old_scrub_pattern);
}
static void finish_scrub(int start_index, uint32_t pattern) {
for (size_t i=start_index; i<sizeof(keystore.keys)/sizeof(keystore.keys.all_keys[0]); i++) {
keystore.mgmt.scrub_backup = keystore.keys.all_keys[i];
keystore.mgmt.scrub_idx_read = i;
keystore.keys.all_keys[i] ^= pattern;
keystore.mgmt.scrub_idx_done = i;
}
}
static void finish_interrupted_scrub(void) {
if (keystore.mgmt.scrub_idx_read != keystore.mgmt.scrub_idx_done)
keystore.keys.all_keys[keystore.mgmt.scrub_idx_read] = keystore.mgmt.scrub_backup;
finish_scrub(keystore.mgmt.scrub_idx_done, keystore.mgmt.old_scrub_pattern ^ keystore.mgmt.new_scrub_pattern);
}
/* setup 10kHz timer */
static void tim6_setup(void) {
timer_reset(TIM6);
timer_set_prescaler(TIM6, 8400 - 1); // 84Mhz/10kHz - 1
timer_set_period(TIM6, 65535); // Overflow in ~6.5 seconds
timer_enable_irq(TIM6, TIM_DIER_UIE);
nvic_enable_irq(NVIC_TIM6_DAC_IRQ);
nvic_set_priority(NVIC_TIM6_DAC_IRQ, 15<<4); /* really low priority */
timer_enable_counter(TIM6);
}
void tim6_dac_isr(void) {
/* Runs every ~6.5s on timer overrun */
timer_clear_flag(TIM6, TIM_SR_UIF);
if (!keystore.mgmt.scrubber_armed)
return;
keystore.mgmt.old_scrub_pattern = keystore.mgmt.new_scrub_pattern;
noise_rand_bytes(&keystore.mgmt.new_scrub_pattern, sizeof(keystore.mgmt.new_scrub_pattern));
LOG_PRINTF("Scrubbing keys using pattern %08x\n", keystore.mgmt.new_scrub_pattern);
finish_scrub(0, keystore.mgmt.old_scrub_pattern ^ keystore.mgmt.new_scrub_pattern);
}
static uint32_t tim6_get_time_us(void)
{
uint32_t cnt = timer_get_counter(TIM6);
// convert to 1MHz less precise timer value -> units: microseconds
uint32_t time_us = cnt * 100;
return time_us;
}
static void gpio_setup(void)
{
/* Tracing */
gpio_mode_setup(GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, 0xffff);
/* D2, D3 LEDs */
//gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO6 | GPIO7);
//gpio_set(GPIOA, GPIO6 | GPIO7);
/* Status LEDs (PE4-15) */
gpio_mode_setup(GPIOE, GPIO_MODE_INPUT, GPIO_PUPD_NONE, 0xfff0);
/* Alarm LEDs (PA6,7) */
gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO6 | GPIO7);
gpio_set(GPIOA, GPIO6 | GPIO7);
/* Speaker */
gpio_mode_setup(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO10);
gpio_set(GPIOB, GPIO10);
/* USB OTG FS phy outputs */
gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO11 | GPIO12);
gpio_set_af(GPIOA, GPIO_AF10, GPIO11 | GPIO12);
/* USART1 (debug) */
gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO9 | GPIO10);
gpio_set_af(GPIOA, GPIO_AF7, GPIO9 | GPIO10);
/* USART2 (host link) */
gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO2 | GPIO3);
gpio_set_af(GPIOA, GPIO_AF7, GPIO2 | GPIO3);
/* K0 (PE4)/K1 (PE3) buttons */
//gpio_mode_setup(GPIOE, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO3 | GPIO4);
}
struct hid_report {
uint8_t modifiers;
uint8_t _reserved;
uint8_t keycodes[6];
} __attribute__((__packed__));
static char pairing_buf[512];
static size_t pairing_buf_pos = 0;
int pairing_check(struct NoiseState *st, const char *buf);
void pairing_input(uint8_t modbyte, uint8_t keycode);
void pairing_parse_report(struct hid_report *buf, uint8_t len);
/* Minimum number of bytes of handshake hash to confirm during pairing */
#define MIN_PAIRING_SEQUENCE_LENGTH 8
int pairing_check(struct NoiseState *st, const char *buf) {
//LOG_PRINTF("Checking pairing\n");
const char *p = buf;
int idx = 0;
do {
/* Skip over most special chars */
while (*p) {
char c = *p;
if ('0' <= c && c <= '9') break;
if ('a' <= c && c <= 'z') break;
if ('A' <= c && c <= 'Z') break;
if (c == '-') break;
p++;
}
const char *found = strchr(p, ' ');
size_t plen = found ? (size_t)(found - p) : strlen(p); /* p >= found */
while (plen > 0) {
char c = p[plen];
if ('0' <= c && c <= '9') break;
if ('a' <= c && c <= 'z') break;
if ('A' <= c && c <= 'Z') break;
if (c == '-') break;
plen--;
}
plen++;
//LOG_PRINTF("matching: \"%s\" - \"%s\" %d\n", p, p+plen, plen);
if (strncasecmp(p, "and", plen)) { /* ignore "and" */
int num = -1;
for (int i=0; i<256; i++) {
if ((!strncasecmp(p, even[i], plen) && plen == strlen(even[i]))
|| (!strncasecmp(p, odd[i], plen) && plen == strlen(odd[i] ))) {
//LOG_PRINTF(" idx=%02d h=%02x i=%02x adj=%s n=%s plen=%d s=%s\n", idx, st->handshake_hash[idx], i, adjectives[i], nouns[i], plen, p);
num = i;
break;
}
}
if (num == -1) {
LOG_PRINTF("Pairing word \"%s\" not found in dictionary\n", p);
return -1;
}
if (st->handshake_hash[idx] != num) {
LOG_PRINTF("Pairing data does not match hash\n");
return -1;
}
idx++;
}
p = strchr(p, ' ');
if (!p)
break; /* end of string */
p++; /* skip space */
} while (idx < BLAKE2S_HASH_SIZE);
if (idx < MIN_PAIRING_SEQUENCE_LENGTH) {
LOG_PRINTF("Pairing sequence too short, only %d bytes of hash checked\n", idx);
return -1;
}
LOG_PRINTF("Pairing sequence match\n");
return 0;
}
void pairing_input(uint8_t modbyte, uint8_t keycode) {
char ch = 0;
uint8_t level = modbyte & MOD_XSHIFT ? LEVEL_SHIFT : LEVEL_NONE;
switch (keycode) {
case KEY_ENTER:
pairing_buf[pairing_buf_pos++] = '\0';
if (!pairing_check(&noise_state, pairing_buf)) {
persist_remote_key(&noise_state);
/* FIXME write key to backup memory */
uint8_t response = REPORT_PAIRING_SUCCESS;
if (send_encrypted_message(&noise_state, &response, sizeof(response)))
LOG_PRINTF("Error sending pairing response packet\n");
} else {
/* FIXME sound alarm */
pairing_buf_pos = 0; /* Reset input buffer */
uint8_t response = REPORT_PAIRING_ERROR;
if (send_encrypted_message(&noise_state, &response, sizeof(response)))
LOG_PRINTF("Error sending pairing response packet\n");
}
break;
case KEY_BACKSPACE:
if (pairing_buf_pos > 0)
pairing_buf_pos--;
pairing_buf[pairing_buf_pos] = '\0';
ch = '\b';
break;
default:
for (size_t i=0; keycode_mapping[i].kc != KEY_NONE; i++) {
if (keycode_mapping[i].kc == keycode) {
ch = keycode_mapping[i].ch[level];
if (!ch)
break;
if (pairing_buf_pos < sizeof(pairing_buf)-1) /* allow for terminating null byte */ {
pairing_buf[pairing_buf_pos++] = ch;
pairing_buf[pairing_buf_pos] = '\0';
} else {
LOG_PRINTF("Pairing confirmation user input buffer full\n");
uint8_t response = REPORT_PAIRING_ERROR;
if (send_encrypted_message(&noise_state, &response, sizeof(response)))
LOG_PRINTF("Error sending pairing response packet\n");
}
break;
}
}
break;
}
if (ch) {
//LOG_PRINTF("Input: %s\n", pairing_buf);
struct hid_report_packet pkt = {
.type = REPORT_PAIRING_INPUT,
.pairing_input = { .c = ch }
};
if (send_encrypted_message(&noise_state, (uint8_t *)&pkt, sizeof(pkt))) {
LOG_PRINTF("Error sending pairing input packet\n");
return;
}
}
}
void pairing_parse_report(struct hid_report *buf, uint8_t len) {
static uint8_t old_keycodes[6] = {0};
for (int i=0; i<len-2; i++) {
if (!buf->keycodes[i])
break; /* keycodes are always populated from low to high */
int found = 0;
for (int j=0; j<6; j++) {
if (old_keycodes[j] == buf->keycodes[i]) {
found = 1;
break;
}
}
if (!found) /* key pressed */
pairing_input(buf->modifiers, buf->keycodes[i]);
}
memcpy(old_keycodes, buf->keycodes, 6);
}
static void hid_in_message_handler(uint8_t device_id, const uint8_t *data, uint32_t length) {
TRACING_SET(TR_HID_MESSAGE_HANDLER);
if (length < 4 || length > 8) {
LOG_PRINTF("HID report length must be 4 < len < 8, is %d bytes\n", length);
TRACING_CLEAR(TR_HID_MESSAGE_HANDLER);
return;
}
//LOG_PRINTF("Sending event %02X %02X %02X %02X\n", data[0], data[1], data[2], data[3]);
int type = hid_get_type(device_id);
if (type != HID_TYPE_KEYBOARD && type != HID_TYPE_MOUSE) {
LOG_PRINTF("Unsupported HID report type %x\n", type);
TRACING_CLEAR(TR_HID_MESSAGE_HANDLER);
return;
}
if (noise_state.handshake_state == HANDSHAKE_DONE_UNKNOWN_HOST) {
if (type == HID_TYPE_KEYBOARD)
pairing_parse_report((struct hid_report *)data, length);
else
LOG_PRINTF("Not sending HID mouse report during pairing\n");
TRACING_CLEAR(TR_HID_MESSAGE_HANDLER);
return;
}
struct hid_report_packet pkt = {
.type = type == HID_TYPE_KEYBOARD ? REPORT_KEYBOARD : REPORT_MOUSE,
.report = {
.len = length,
.report = {0}
}
};
memcpy(pkt.report.report, data, length);
if (send_encrypted_message(&noise_state, (uint8_t *)&pkt, sizeof(pkt))) {
LOG_PRINTF("Error sending HID report packet\n");
TRACING_CLEAR(TR_HID_MESSAGE_HANDLER);
return;
}
TRACING_CLEAR(TR_HID_MESSAGE_HANDLER);
}
volatile struct {
struct dma_buf dma;
uint8_t data[256];
} debug_buf = { .dma = { .len = sizeof(debug_buf.data) } };
struct dma_usart_file debug_out_s = {
.usart = DEBUG_USART,
.baudrate = DEBUG_USART_BAUDRATE,
.dma = DMA(DEBUG_USART_DMA_NUM),
.stream = DEBUG_USART_DMA_STREAM_NUM,
.channel = DEBUG_USART_DMA_CHANNEL_NUM,
.irqn = NVIC_DMA_IRQ(DEBUG_USART_DMA_NUM, DEBUG_USART_DMA_STREAM_NUM),
.buf = &debug_buf.dma
};
struct dma_usart_file *debug_out = &debug_out_s;
/* FIXME start unsafe debug code */
void usart1_isr(void) {
if (USART1_SR & USART_SR_ORE) { /* Overrun handling */
LOG_PRINTF("USART1 data register overrun\n");
/* Clear interrupt flag */
int dummy = USART1_DR;
return;
}
uint8_t data = USART1_DR; /* This automatically acknowledges the IRQ */
for (size_t i=0; keycode_mapping[i].kc != KEY_NONE; i++) {
struct hid_report report = {0};
if (keycode_mapping[i].ch[0] == data)
report.modifiers = 0;
else if (keycode_mapping[i].ch[1] == data)
report.modifiers = MOD_LSHIFT;
else continue;
report.keycodes[0] = keycode_mapping[i].kc;
pairing_parse_report(&report, 8);
break;
}
LOG_PRINTF(" %02x ", data);
if (data == 0x7f) {
struct hid_report report = {.modifiers=0, .keycodes={KEY_BACKSPACE, 0}};
pairing_parse_report(&report, 8);
} else if (data == '\r') {
struct hid_report report = {.modifiers=0, .keycodes={KEY_ENTER, 0}};
pairing_parse_report(&report, 8);
LOG_PRINTF("\n");
}
struct hid_report report = {0};
pairing_parse_report(&report, 8);
}
/* end unsafe debug code */
void DMA_ISR(DEBUG_USART_DMA_NUM, DEBUG_USART_DMA_STREAM_NUM)(void) {
TRACING_SET(TR_DEBUG_OUT_DMA_IRQ);
if (dma_get_interrupt_flag(debug_out->dma, debug_out->stream, DMA_FEIF)) {
/* Ignore FIFO errors as they're 100% non-critical for UART applications */
dma_clear_interrupt_flags(debug_out->dma, debug_out->stream, DMA_FEIF);
TRACING_CLEAR(TR_DEBUG_OUT_DMA_IRQ);
return;
}
/* Transfer complete */
dma_clear_interrupt_flags(debug_out->dma, debug_out->stream, DMA_TCIF);
if (debug_out->buf->wr_pos != debug_out->buf->xfr_end) /* buffer not empty */
schedule_dma(debug_out);
TRACING_CLEAR(TR_DEBUG_OUT_DMA_IRQ);
}
/*@ requires \valid_read(&pkt->type) && \valid_read(pkt->payload + (0..payload_length-1));
requires \valid(st);
requires \valid(st->handshake);
requires \separated(st, st->rx_cipher, st->tx_cipher, st->handshake, (uint8_t *)pkt->payload, &usart2_out, &st->handshake_hash);
requires \valid(usart2_out);
assigns pairing_buf_pos, *usart2_out, *st;
assigns st->handshake, st->handshake_state, st->rx_cipher, st->tx_cipher;
@*/
void handle_host_packet(struct NoiseState *st, const struct control_packet *pkt, size_t payload_length) {
TRACING_SET(TR_HOST_PKT_HANDLER);
if (pkt->type == HOST_INITIATE_HANDSHAKE) {
/* It is important that we acknowledge this command right away. Starting the handshake involves key
* generation which takes a few milliseconds. If we'd acknowledge this later, we might run into an
* overrun here since we would be blocking the buffer during key generation. */
if (payload_length > 0) {
LOG_PRINTF("Extraneous data in INITIATE_HANDSHAKE message\n");
} else if (st->failed_handshakes < MAX_FAILED_HANDSHAKES) {
LOG_PRINTF("Starting noise protocol handshake...\n");
if (reset_protocol_handshake(st))
LOG_PRINTF("Error starting protocol handshake.\n");
pairing_buf_pos = 0; /* Reset channel binding keyboard input buffer */
} else {
LOG_PRINTF("Too many failed handshake attempts, not starting another one\n");
struct control_packet out = { .type=HOST_TOO_MANY_FAILS };
send_packet(usart2_out, (uint8_t *)&out, sizeof(out));
}
} else if (pkt->type == HOST_HANDSHAKE) {
LOG_PRINTF("Handling handshake packet of length %d\n", payload_length);
TRACING_SET(TR_NOISE_HANDSHAKE);
if (try_continue_noise_handshake(st, pkt->payload, payload_length)) {
TRACING_CLEAR(TR_NOISE_HANDSHAKE);
LOG_PRINTF("Reporting handshake error to host\n");
struct control_packet out = { .type=HOST_CRYPTO_ERROR };
send_packet(usart2_out, (uint8_t *)&out, sizeof(out));
} else TRACING_CLEAR(TR_NOISE_HANDSHAKE);
} else {
LOG_PRINTF("Unhandled packet of type %d\n", pkt->type);
}
TRACING_CLEAR(TR_HOST_PKT_HANDLER);
}
int main(void)
{
clock_setup();
gpio_setup();
pwr_disable_backup_domain_write_protect();
PWR_CSR |= PWR_CSR_BRE; /* Enable backup SRAM battery power regulator */
finish_interrupted_scrub();
disarm_key_scrubber();
tim6_setup();
#ifdef USART_DEBUG
usart_dma_init(debug_out);
/* FIXME start unsafe debug code */
usart_enable_rx_interrupt(debug_out->usart);
nvic_enable_irq(NVIC_USART1_IRQ);
nvic_set_priority(NVIC_USART1_IRQ, 3<<4);
/* end unsafe debug code */
#endif
usart_dma_init(usart2_out);
usart_enable_rx_interrupt(USART2);
nvic_enable_irq(NVIC_USART2_IRQ);
nvic_set_priority(NVIC_USART2_IRQ, 3<<4);
nvic_set_priority(debug_out_s.irqn, 1<<4);
LOG_PRINTF("\n==================================\n");
LOG_PRINTF("SecureHID device side initializing\n");
LOG_PRINTF("==================================\n");
LOG_PRINTF("Initializing USB...\n");
const hid_config_t hid_config = { .hid_in_message_handler = &hid_in_message_handler };
hid_driver_init(&hid_config);
hub_driver_init();
const usbh_dev_driver_t *device_drivers[] = { &usbh_hub_driver, &usbh_hid_driver, NULL };
const usbh_low_level_driver_t * const lld_drivers[] = { &usbh_lld_stm32f4_driver_fs, NULL };
usbh_init(lld_drivers, device_drivers);
LOG_PRINTF("Initializing RNG...\n");
rand_init();
//@ assert \valid(&noise_state);
//@ assert \valid((uint8_t *)keystore.keys.remote_key_reference + (0..31)) && \valid((uint8_t *)keystore.keys.local_key + (0..31));
noise_state_init(&noise_state, keystore.keys.remote_key_reference, keystore.keys.local_key);
//@ assert \valid(noise_state.local_key + (0..31));
/* FIXME load remote key from backup memory */
/* FIXME only run this on first boot and persist key in backup sram. Allow reset via jumper-triggered factory reset function. */
if (!keystore.mgmt.identity_key_valid) {
LOG_PRINTF("Generating identity key...\n");
if (generate_identity_key(&noise_state)) {
LOG_PRINTF("Error generating identiy key\n");
} else {
keystore.mgmt.identity_key_valid = 1;
}
}
int poll_ctr = 0;
int led_ctr = 0;
int led_idx = 0;
int spk_ctr = 0;
int spk_ctr2 = 0;
int spk_adv = 0;
int spk_inc = 1;
gpio_clear(GPIOA, GPIO6);
gpio_clear(GPIOA, GPIO7);
gpio_clear(GPIOB, GPIO10);
while (23) {
delay(1);
led_ctr++;
if (led_ctr == 10) {
gpio_clear(GPIOA, GPIO6);
gpio_clear(GPIOA, GPIO7);
} else if (led_ctr == 300) {
gpio_mode_setup(GPIOE, GPIO_MODE_INPUT, GPIO_PUPD_NONE, 0xfff0);
} else if (led_ctr == 400) {
if (++led_idx == 12)
led_idx = 0;
gpio_mode_setup(GPIOE, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, 1<<(4+led_idx));
gpio_clear(GPIOE, 0xfff0);
if (led_idx & 1)
gpio_set(GPIOA, GPIO6);
else
gpio_set(GPIOA, GPIO7);
led_ctr = 0;
}
spk_ctr++;
spk_ctr2++;
if (spk_ctr2 == 100) {
spk_adv += spk_inc;
if (spk_adv > 31)
spk_inc = -3;
if (spk_adv < 1)
spk_inc = 1;
spk_ctr2 = 0;
}
if (spk_ctr%spk_adv == 0) {
gpio_set(GPIOB, GPIO10);
} else {
gpio_clear(GPIOB, GPIO10);
}
continue;
if (++poll_ctr == 10) {
poll_ctr = 0;
TRACING_SET(TR_USBH_POLL);
usbh_poll(tim6_get_time_us());
TRACING_CLEAR(TR_USBH_POLL);
}
if (host_packet_length > 0) {
handle_host_packet(&noise_state, (struct control_packet *)host_packet_buf, host_packet_length - 1);
host_packet_length = 0; /* Acknowledge to USART ISR the buffer has been handled */
} else if (host_packet_length < 0) { /* USART error */
host_packet_length = 0; /* Acknowledge to USART ISR the error has been handled */
if (noise_state.handshake_state < HANDSHAKE_DONE_UNKNOWN_HOST) {
LOG_PRINTF("USART error, aborting handshake\n");
struct control_packet pkt = { .type=HOST_COMM_ERROR };
send_packet(usart2_out, (uint8_t *)&pkt, sizeof(pkt));
if (reset_protocol_handshake(&noise_state))
LOG_PRINTF("Error starting protocol handshake.\n");
pairing_buf_pos = 0; /* Reset channel binding keyboard input buffer */
}
}
if (noise_state.handshake_state == HANDSHAKE_IN_PROGRESS) {
TRACING_SET(TR_NOISE_HANDSHAKE);
if (try_continue_noise_handshake(&noise_state, NULL, 0)) { /* handle outgoing messages */
TRACING_CLEAR(TR_NOISE_HANDSHAKE);
LOG_PRINTF("Reporting handshake error to host\n");
struct control_packet pkt = { .type=HOST_CRYPTO_ERROR };
send_packet(usart2_out, (uint8_t *)&pkt, sizeof(pkt));
} else TRACING_CLEAR(TR_NOISE_HANDSHAKE);
}
}
}
void _fini() {
while (1);
}

1
fw/src/frama_c_cmdline Normal file
View file

@ -0,0 +1 @@
frama-c-gui -c11 -cpp-extra-args="-DVERIFICATION -DSTM32F4 -DUSE_STM32F4_USBH_DRIVER_FS -DDEBUG_USART=USART1 -DDEBUG_USART_BAUDRATE=115200 -DDEBUG_USART_DMA_CHANNEL_NUM=4 -DDEBUG_USART_DMA_NUM=2 -DDEBUG_USART_DMA_STREAM_NUM=7 -I. -I"(frama-c -print-path)"/libc -I crypto/noise-c/include -I../include -I../libopencm3/include" -wp -wp-rte -wp-model +cint -wp-verbose 2 -slevel 8 -wp-out goals noise.c demo.c -wp-fct try_continue_noise_handshake

48
fw/src/hid_keycodes.c Normal file
View file

@ -0,0 +1,48 @@
#include "hid_keycodes.h"
struct keymap_entry keycode_mapping[] = {
{ KEY_A, {'a', 'A'}},
{ KEY_B, {'b', 'B'}},
{ KEY_C, {'c', 'C'}},
{ KEY_D, {'d', 'D'}},
{ KEY_E, {'e', 'E'}},
{ KEY_F, {'f', 'F'}},
{ KEY_G, {'g', 'G'}},
{ KEY_H, {'h', 'H'}},
{ KEY_I, {'i', 'I'}},
{ KEY_J, {'j', 'J'}},
{ KEY_K, {'k', 'K'}},
{ KEY_L, {'l', 'L'}},
{ KEY_M, {'m', 'M'}},
{ KEY_N, {'n', 'N'}},
{ KEY_O, {'o', 'O'}},
{ KEY_P, {'p', 'P'}},
{ KEY_Q, {'q', 'Q'}},
{ KEY_R, {'r', 'R'}},
{ KEY_S, {'s', 'S'}},
{ KEY_T, {'t', 'T'}},
{ KEY_U, {'u', 'U'}},
{ KEY_V, {'v', 'V'}},
{ KEY_W, {'w', 'W'}},
{ KEY_X, {'x', 'X'}},
{ KEY_Y, {'y', 'Y'}},
{ KEY_Z, {'z', 'Z'}},
{ KEY_1, {'1', '!'}},
{ KEY_2, {'2', '@'}},
{ KEY_3, {'3', '#'}},
{ KEY_4, {'4', '$'}},
{ KEY_5, {'5', '%'}},
{ KEY_6, {'6', '^'}},
{ KEY_7, {'7', '&'}},
{ KEY_8, {'8', '*'}},
{ KEY_9, {'9', '('}},
{ KEY_0, {'0', ')'}},
{ KEY_MINUS, {'-', '_'}},
{ KEY_SPACE, {' ', ' '}},
{ KEY_COMMA, {',', '<'}},
{ KEY_DOT, {'.', '>'}},
{ KEY_SEMICOLON, {';', ':'}},
{ KEY_NONE, {0, 0}}, /* end marker */
};

218
fw/src/hid_keycodes.h Normal file
View file

@ -0,0 +1,218 @@
#ifndef __HID_KEYCODES_H__
#define __HID_KEYCODES_H__
enum mod_levels {
LEVEL_NONE,
LEVEL_SHIFT,
LEVEL_NLEVELS
};
enum mod_bits {
MOD_LCTRL,
MOD_LSHIFT,
MOD_LALT,
MOD_LMETA,
MOD_RCTRL,
MOD_RSHIFT,
MOD_RALT,
MOD_RMETA,
};
enum mod_bitmaps {
MOD_XCTRL = MOD_LCTRL | MOD_RCTRL,
MOD_XSHIFT = MOD_LSHIFT | MOD_RSHIFT,
MOD_XALT = MOD_LALT | MOD_RALT,
MOD_XMETA = MOD_LMETA | MOD_RMETA,
};
struct keymap_entry {
unsigned char kc;
char ch[LEVEL_NLEVELS];
};
extern struct keymap_entry keycode_mapping[];
enum hid_keycode {
KEY_NONE = 0x00, // No key pressed
KEY_ERR_OVF = 0x01, // Keyboard Error Roll Over - used for all slots if too many keys are pressed ("Phantom key")
KEY_A = 0x04, // Keyboard a and A
KEY_B = 0x05, // Keyboard b and B
KEY_C = 0x06, // Keyboard c and C
KEY_D = 0x07, // Keyboard d and D
KEY_E = 0x08, // Keyboard e and E
KEY_F = 0x09, // Keyboard f and F
KEY_G = 0x0a, // Keyboard g and G
KEY_H = 0x0b, // Keyboard h and H
KEY_I = 0x0c, // Keyboard i and I
KEY_J = 0x0d, // Keyboard j and J
KEY_K = 0x0e, // Keyboard k and K
KEY_L = 0x0f, // Keyboard l and L
KEY_M = 0x10, // Keyboard m and M
KEY_N = 0x11, // Keyboard n and N
KEY_O = 0x12, // Keyboard o and O
KEY_P = 0x13, // Keyboard p and P
KEY_Q = 0x14, // Keyboard q and Q
KEY_R = 0x15, // Keyboard r and R
KEY_S = 0x16, // Keyboard s and S
KEY_T = 0x17, // Keyboard t and T
KEY_U = 0x18, // Keyboard u and U
KEY_V = 0x19, // Keyboard v and V
KEY_W = 0x1a, // Keyboard w and W
KEY_X = 0x1b, // Keyboard x and X
KEY_Y = 0x1c, // Keyboard y and Y
KEY_Z = 0x1d, // Keyboard z and Z
KEY_1 = 0x1e, // Keyboard 1 and !
KEY_2 = 0x1f, // Keyboard 2 and @
KEY_3 = 0x20, // Keyboard 3 and #
KEY_4 = 0x21, // Keyboard 4 and $
KEY_5 = 0x22, // Keyboard 5 and %
KEY_6 = 0x23, // Keyboard 6 and ^
KEY_7 = 0x24, // Keyboard 7 and &
KEY_8 = 0x25, // Keyboard 8 and *
KEY_9 = 0x26, // Keyboard 9 and (
KEY_0 = 0x27, // Keyboard 0 and )
KEY_ENTER = 0x28, // Keyboard Return (ENTER)
KEY_ESC = 0x29, // Keyboard ESCAPE
KEY_BACKSPACE = 0x2a, // Keyboard DELETE (Backspace)
KEY_TAB = 0x2b, // Keyboard Tab
KEY_SPACE = 0x2c, // Keyboard Spacebar
KEY_MINUS = 0x2d, // Keyboard - and _
KEY_EQUAL = 0x2e, // Keyboard = and +
KEY_LEFTBRACE = 0x2f, // Keyboard [ and {
KEY_RIGHTBRACE = 0x30, // Keyboard ] and }
KEY_BACKSLASH = 0x31, // Keyboard \ and |
KEY_HASHTILDE = 0x32, // Keyboard Non-US # and ~
KEY_SEMICOLON = 0x33, // Keyboard ; and :
KEY_APOSTROPHE = 0x34, // Keyboard ' and "
KEY_GRAVE = 0x35, // Keyboard ` and ~
KEY_COMMA = 0x36, // Keyboard , and <
KEY_DOT = 0x37, // Keyboard . and >
KEY_SLASH = 0x38, // Keyboard / and ?
KEY_CAPSLOCK = 0x39, // Keyboard Caps Lock
KEY_F1 = 0x3a, // Keyboard F1
KEY_F2 = 0x3b, // Keyboard F2
KEY_F3 = 0x3c, // Keyboard F3
KEY_F4 = 0x3d, // Keyboard F4
KEY_F5 = 0x3e, // Keyboard F5
KEY_F6 = 0x3f, // Keyboard F6
KEY_F7 = 0x40, // Keyboard F7
KEY_F8 = 0x41, // Keyboard F8
KEY_F9 = 0x42, // Keyboard F9
KEY_F10 = 0x43, // Keyboard F10
KEY_F11 = 0x44, // Keyboard F11
KEY_F12 = 0x45, // Keyboard F12
KEY_SYSRQ = 0x46, // Keyboard Print Screen
KEY_SCROLLLOCK = 0x47, // Keyboard Scroll Lock
KEY_PAUSE = 0x48, // Keyboard Pause
KEY_INSERT = 0x49, // Keyboard Insert
KEY_HOME = 0x4a, // Keyboard Home
KEY_PAGEUP = 0x4b, // Keyboard Page Up
KEY_DELETE = 0x4c, // Keyboard Delete Forward
KEY_END = 0x4d, // Keyboard End
KEY_PAGEDOWN = 0x4e, // Keyboard Page Down
KEY_RIGHT = 0x4f, // Keyboard Right Arrow
KEY_LEFT = 0x50, // Keyboard Left Arrow
KEY_DOWN = 0x51, // Keyboard Down Arrow
KEY_UP = 0x52, // Keyboard Up Arrow
KEY_NUMLOCK = 0x53, // Keyboard Num Lock and Clear
KEY_KPSLASH = 0x54, // Keypad /
KEY_KPASTERISK = 0x55, // Keypad *
KEY_KPMINUS = 0x56, // Keypad -
KEY_KPPLUS = 0x57, // Keypad +
KEY_KPENTER = 0x58, // Keypad ENTER
KEY_KP1 = 0x59, // Keypad 1 and End
KEY_KP2 = 0x5a, // Keypad 2 and Down Arrow
KEY_KP3 = 0x5b, // Keypad 3 and PageDn
KEY_KP4 = 0x5c, // Keypad 4 and Left Arrow
KEY_KP5 = 0x5d, // Keypad 5
KEY_KP6 = 0x5e, // Keypad 6 and Right Arrow
KEY_KP7 = 0x5f, // Keypad 7 and Home
KEY_KP8 = 0x60, // Keypad 8 and Up Arrow
KEY_KP9 = 0x61, // Keypad 9 and Page Up
KEY_KP0 = 0x62, // Keypad 0 and Insert
KEY_KPDOT = 0x63, // Keypad . and Delete
KEY_102ND = 0x64, // Keyboard Non-US \ and |
KEY_COMPOSE = 0x65, // Keyboard Application
KEY_POWER = 0x66, // Keyboard Power
KEY_KPEQUAL = 0x67, // Keypad =
KEY_F13 = 0x68, // Keyboard F13
KEY_F14 = 0x69, // Keyboard F14
KEY_F15 = 0x6a, // Keyboard F15
KEY_F16 = 0x6b, // Keyboard F16
KEY_F17 = 0x6c, // Keyboard F17
KEY_F18 = 0x6d, // Keyboard F18
KEY_F19 = 0x6e, // Keyboard F19
KEY_F20 = 0x6f, // Keyboard F20
KEY_F21 = 0x70, // Keyboard F21
KEY_F22 = 0x71, // Keyboard F22
KEY_F23 = 0x72, // Keyboard F23
KEY_F24 = 0x73, // Keyboard F24
KEY_OPEN = 0x74, // Keyboard Execute
KEY_HELP = 0x75, // Keyboard Help
KEY_PROPS = 0x76, // Keyboard Menu
KEY_FRONT = 0x77, // Keyboard Select
KEY_STOP = 0x78, // Keyboard Stop
KEY_AGAIN = 0x79, // Keyboard Again
KEY_UNDO = 0x7a, // Keyboard Undo
KEY_CUT = 0x7b, // Keyboard Cut
KEY_COPY = 0x7c, // Keyboard Copy
KEY_PASTE = 0x7d, // Keyboard Paste
KEY_FIND = 0x7e, // Keyboard Find
KEY_MUTE = 0x7f, // Keyboard Mute
KEY_VOLUMEUP = 0x80, // Keyboard Volume Up
KEY_VOLUMEDOWN = 0x81, // Keyboard Volume Down
KEY_KPCOMMA = 0x85, // Keypad Comma
KEY_RO = 0x87, // Keyboard International1
KEY_KATAKANAHIRAGANA = 0x88, // Keyboard International2
KEY_YEN = 0x89, // Keyboard International3
KEY_HENKAN = 0x8a, // Keyboard International4
KEY_MUHENKAN = 0x8b, // Keyboard International5
KEY_KPJPCOMMA = 0x8c, // Keyboard International6
KEY_HANGEUL = 0x90, // Keyboard LANG1
KEY_HANJA = 0x91, // Keyboard LANG2
KEY_KATAKANA = 0x92, // Keyboard LANG3
KEY_HIRAGANA = 0x93, // Keyboard LANG4
KEY_ZENKAKUHANKAKU = 0x94, // Keyboard LANG5
KEY_KPLEFTPAREN = 0xb6, // Keypad (
KEY_KPRIGHTPAREN = 0xb7, // Keypad )
KEY_LEFTCTRL = 0xe0, // Keyboard Left Control
KEY_LEFTSHIFT = 0xe1, // Keyboard Left Shift
KEY_LEFTALT = 0xe2, // Keyboard Left Alt
KEY_LEFTMETA = 0xe3, // Keyboard Left GUI
KEY_RIGHTCTRL = 0xe4, // Keyboard Right Control
KEY_RIGHTSHIFT = 0xe5, // Keyboard Right Shift
KEY_RIGHTALT = 0xe6, // Keyboard Right Alt
KEY_RIGHTMETA = 0xe7, // Keyboard Right GUI
KEY_MEDIA_PLAYPAUSE = 0xe8,
KEY_MEDIA_STOPCD = 0xe9,
KEY_MEDIA_PREVIOUSSONG = 0xea,
KEY_MEDIA_NEXTSONG = 0xeb,
KEY_MEDIA_EJECTCD = 0xec,
KEY_MEDIA_VOLUMEUP = 0xed,
KEY_MEDIA_VOLUMEDOWN = 0xee,
KEY_MEDIA_MUTE = 0xef,
KEY_MEDIA_WWW = 0xf0,
KEY_MEDIA_BACK = 0xf1,
KEY_MEDIA_FORWARD = 0xf2,
KEY_MEDIA_STOP = 0xf3,
KEY_MEDIA_FIND = 0xf4,
KEY_MEDIA_SCROLLUP = 0xf5,
KEY_MEDIA_SCROLLDOWN = 0xf6,
KEY_MEDIA_EDIT = 0xf7,
KEY_MEDIA_SLEEP = 0xf8,
KEY_MEDIA_COFFEE = 0xf9,
KEY_MEDIA_REFRESH = 0xfa,
KEY_MEDIA_CALC = 0xfb,
};
#endif

445
fw/src/noise.c Normal file
View file

@ -0,0 +1,445 @@
#include <string.h>
#include "noise.h"
#include "packet_interface.h"
#include "rand_stm32.h"
#include "crypto/noise-c/src/crypto/blake2/blake2s.h"
#ifdef VERIFICATION
#define HANDLE_NOISE_ERROR(x, msg) if (x) { goto errout; }
#else
#define HANDLE_NOISE_ERROR(x, msg) do { \
err = x; \
if (err != NOISE_ERROR_NONE) { \
char errbuf[256]; \
noise_strerror(err, errbuf, sizeof(errbuf)); \
LOG_PRINTF("Error " msg ": %s\n", errbuf); \
goto errout; \
} \
} while(0);
#endif
#ifdef VERIFICATION
/*@ requires \valid(s + (0..n-1));
@ assigns s[0..n-1] \from c;
@ assigns \result \from s;
@ ensures result_ptr: \result == s;
@*/
uint8_t *fc_memset_uint8(uint8_t *s, int c, size_t n);
/*@ requires \valid(dest + (0..n-1));
@ requires \valid_read(src + (0..n-1));
@ assigns dest[0..n-1] \from src[0..n-1];
@ assigns \result \from dest;
@ ensures result_ptr: \result == dest;
@ ensures equals: \forall integer i; 0 <= i <= n-1 ==> dest[i] == src[i];
@*/
uint8_t *fc_memcpy_uint8(uint8_t *dest, const uint8_t *src, size_t n);
/*@ requires valid_s1: \valid_read(s1 + (0..n-1));
@ requires valid_s2: \valid_read(s2 + (0..n-1));
@ assigns \result \from
@ indirect:s1[0.. n-1], indirect:s2[0.. n-1];
@ ensures logic_spec: \result == 0 <==> (
\forall integer i; 0 <= i <= n-1 ==> s1[i] == s2[i]);
@*/
int fc_memcmp_uint8(const uint8_t *s1, const uint8_t *s2, size_t n);
#else
#define fc_memset_uint8 memset
#define fc_memcpy_uint8 memcpy
#define fc_memcmp_uint8 memcmp
#endif
volatile uint8_t host_packet_buf[MAX_HOST_PACKET_SIZE];
volatile int host_packet_length = 0;
/*@
requires validity: \valid(st);
ensures equal: st->remote_key_reference == remote_key_reference && st->local_key == local_key;
ensures equal: st->handshake_state == HANDSHAKE_UNINITIALIZED;
ensures equal: st->failed_handshakes == 0;
ensures equal: st->tx_cipher == NULL && st->rx_cipher == NULL && st->handshake == NULL;
assigns *st;
*/
void noise_state_init(struct NoiseState *st, uint8_t *remote_key_reference, uint8_t *local_key) {
st->handshake_state = HANDSHAKE_UNINITIALIZED;
st->handshake = NULL;
st->tx_cipher = NULL;
st->rx_cipher = NULL;
fc_memset_uint8(st->handshake_hash, 0, sizeof(st->handshake_hash));
st->remote_key_reference = remote_key_reference;
st->local_key = local_key;
st->failed_handshakes = 0;
}
/*@
requires validity: \valid(st) && \valid(st->handshake_hash + (0..31)) && \valid_read(st->local_key + (0..31));
requires separation: \separated(st, st->rx_cipher, st->tx_cipher, st->handshake);
ensures result: \result \in {0, -1};
ensures success: \result == 0 ==> (
\valid(st->handshake) &&
(st->handshake_state == HANDSHAKE_PHASE1));
ensures failure: \result != 0 ==> (
(st->handshake == NULL) &&
(st->handshake_state == HANDSHAKE_UNINITIALIZED));
ensures unmodified: \old(st->failed_handshakes) == st->failed_handshakes;
assigns *st, *st->rx_cipher, *st->tx_cipher;
*/
int reset_protocol_handshake(struct NoiseState *st) {
uninit_handshake(st, HANDSHAKE_UNINITIALIZED);
disarm_key_scrubber();
noise_cipherstate_free(st->tx_cipher);
noise_cipherstate_free(st->rx_cipher);
st->tx_cipher = NULL;
st->rx_cipher = NULL;
st->handshake = NULL;
fc_memset_uint8(st->handshake_hash, 0, sizeof(st->handshake_hash));
return start_protocol_handshake(st);
}
/*@ requires validity: \valid(st) && \valid_read(st->local_key + (0..31));
ensures result: \result \in {0, -1};
ensures success: \result == 0 ==> (
\valid(st->handshake) &&
st->handshake_state == HANDSHAKE_PHASE1);
ensures failure: \result != 0 ==> (
st->handshake == \old(st->handshake) &&
st->handshake_state == \old(st->handshake_state));
assigns st->handshake, st->handshake_state;
*/
int start_protocol_handshake(struct NoiseState *st) {
/* TODO Noise-C is nice for prototyping, but we should really get rid of it for mostly three reasons:
* * We don't need cipher/protocol agility, and by baking the final protocol into the firmware we can save a lot
* of flash space by not including all the primitives we don't need as well as noise's dynamic protocol
* abstraction layer.
* * Noise-c is not very embedded-friendly, in particular it uses malloc and free. We should be able to run
* everything with statically allocated buffers instead.
* * Parts of it are not written that well
*/
NoiseHandshakeState *handshake;
int err;
HANDLE_NOISE_ERROR(noise_init(), "initializing noise");
HANDLE_NOISE_ERROR(noise_handshakestate_new_by_name(&handshake, "Noise_XX_25519_ChaChaPoly_BLAKE2s", NOISE_ROLE_RESPONDER), "instantiating handshake pattern");
NoiseDHState *dh = noise_handshakestate_get_local_keypair_dh(handshake);
HANDLE_NOISE_ERROR(noise_dhstate_set_keypair_private(dh, st->local_key, CURVE25519_KEY_LEN), "loading local private keys");
HANDLE_NOISE_ERROR(noise_handshakestate_start(handshake), "starting handshake");
st->handshake = handshake;
st->handshake_state = HANDSHAKE_PHASE1;
return 0;
errout:
noise_handshakestate_free(handshake);
return -1;
}
/*@ requires validity: \valid(st) && \valid(st->local_key + (0..31));
assigns st->local_key[0..31];
*/
int generate_identity_key(struct NoiseState *st) {
NoiseDHState *dh;
int err;
HANDLE_NOISE_ERROR(noise_dhstate_new_by_name(&dh, "25519"), "creating dhstate for key generation");
HANDLE_NOISE_ERROR(noise_dhstate_generate_keypair(dh), "generating key pair");
uint8_t unused[CURVE25519_KEY_LEN]; /* the noise api is a bit bad here. */
fc_memset_uint8(st->local_key, 0, CURVE25519_KEY_LEN);
HANDLE_NOISE_ERROR(noise_dhstate_get_keypair(dh, st->local_key, CURVE25519_KEY_LEN, unused, sizeof(unused)), "saving key pair");
noise_dhstate_free(dh);
return 0;
errout:
if (dh)
noise_dhstate_free(dh);
return -1;
}
/*@requires validity: \valid(st);
requires state_valid: new_state \in
{HANDSHAKE_UNINITIALIZED, HANDSHAKE_DONE_UNKNOWN_HOST, HANDSHAKE_DONE_KNOWN_HOST};
ensures state: st->handshake_state == new_state;
ensures handshake: st->handshake == NULL;
assigns st->handshake, st->handshake_state;
@*/
void uninit_handshake(struct NoiseState *st, enum handshake_state new_state) {
if (st->handshake)
noise_handshakestate_free(st->handshake);
st->handshake_state = new_state;
st->handshake = NULL;
arm_key_scrubber();
}
/*@
requires validity: \valid(st) && \valid(usart2_out) && \valid(st->handshake);
requires validity: \valid(st->remote_key + (0..sizeof(st->remote_key)-1));
requires validity: \valid(st->handshake_hash + (0..sizeof(st->handshake_hash)-1));
requires separation: \separated(usart2_out, st, buf, st->handshake);
ensures \result \in {-1, 0};
assigns *usart2_out, *st->handshake;
@*/
int handshake_phase1(struct NoiseState * const st, uint8_t *buf, size_t len) {
int err;
struct {
struct control_packet header;
uint8_t payload[MAX_HOST_PACKET_SIZE];
} pkt;
NoiseBuffer noise_msg;
/* Read the next handshake message and discard the payload */
noise_buffer_set_input(noise_msg, buf, len);
HANDLE_NOISE_ERROR(noise_handshakestate_read_message(st->handshake, &noise_msg, NULL), "reading handshake message");
/* Write the next handshake message with a zero-length noise payload */
pkt.header.type = HOST_HANDSHAKE;
noise_buffer_set_output(noise_msg, &pkt.payload, sizeof(pkt.payload));
HANDLE_NOISE_ERROR(noise_handshakestate_write_message(st->handshake, &noise_msg, NULL), "writing handshake message");
send_packet(usart2_out, (uint8_t *)&pkt, noise_msg.size + sizeof(pkt.header));
return 0;
errout: /* for HANDLE_NOISE_ERROR macro */
return -1;
}
//@ ghost int key_checked_trace;
//@ ghost int key_match_trace;
/*@
requires validity: \valid(st) && \valid(usart2_out) && \valid(st->handshake);
requires validity: \valid(st->remote_key + (0..sizeof(st->remote_key)-1));
requires validity: \valid_read(st->remote_key_reference + (0..sizeof(st->remote_key)-1));
requires validity: \valid(st->handshake_hash + (0..sizeof(st->handshake_hash)-1));
requires separation: \separated(usart2_out, st);
requires sanity: 0 <= st->failed_handshakes < 100;
requires separation: \separated(&usart2_out, st, buf, st->handshake);
ensures result: \result \in {-1, 0, 1};
ensures sanity: 0 <= st->failed_handshakes <= 102;
ensures sanity: \result != 1 ==> st->failed_handshakes >= \old(st->failed_handshakes);
ensures permission_valid: \result != -1 ==> key_checked_trace == 1;
ensures permission_valid: \result == 1 ==> key_match_trace == 1;
//
assigns *usart2_out, *st, *st->handshake, key_checked_trace, key_match_trace;
@*/
int handshake_phase2(struct NoiseState * const st, uint8_t *buf, size_t len) {
//@ ghost int old_failed_handshakes = st->failed_handshakes;
int err;
struct {
struct control_packet header;
uint8_t payload[MAX_HOST_PACKET_SIZE];
} pkt;
NoiseBuffer noise_msg;
//@ ghost key_checked_trace = 0;
// ghost key_match_trace = 0;
/* Read the next handshake message and discard the payload */
noise_buffer_set_input(noise_msg, buf, len);
HANDLE_NOISE_ERROR(noise_handshakestate_read_message(st->handshake, &noise_msg, NULL), "reading handshake message");
HANDLE_NOISE_ERROR(noise_handshakestate_split(st->handshake, &st->tx_cipher, &st->rx_cipher), "splitting handshake state");
LOG_PRINTF("Noise protocol handshake completed successfully, handshake hash:\n");
if (noise_handshakestate_get_handshake_hash(st->handshake, st->handshake_hash, sizeof(st->handshake_hash)) != NOISE_ERROR_NONE) {
LOG_PRINTF("Error fetching noise handshake state\n");
goto errout;
} else {
LOG_PRINTF(" ");
/*@ loop assigns i; @*/
for (size_t i=0; i<sizeof(st->handshake_hash); i++)
LOG_PRINTF("%02x ", st->handshake_hash[i]);
LOG_PRINTF("\n");
}
NoiseDHState *remote_dh = noise_handshakestate_get_remote_public_key_dh(st->handshake);
if (!remote_dh) {
LOG_PRINTF("Error: Host has not identified itself\n");
goto errout;
}
HANDLE_NOISE_ERROR(noise_dhstate_get_public_key(remote_dh, st->remote_key, sizeof(st->remote_key)), "getting remote pubkey");
/* TODO support list of known remote hosts here instead of just one */
uint8_t remote_fp[BLAKE2S_HASH_SIZE];
BLAKE2s_context_t bc;
BLAKE2s_reset(&bc);
fc_BLAKE2s_update_uint8(&bc, st->remote_key, sizeof(st->remote_key));
BLAKE2s_finish(&bc, remote_fp);
//@ ghost key_checked_trace = 1;
if (!fc_memcmp_uint8(remote_fp, st->remote_key_reference, sizeof(remote_fp))) { /* keys match */
//@ ghost key_match_trace = 1;
uint8_t response = REPORT_PAIRING_SUCCESS;
if (send_encrypted_message(st, &response, sizeof(response)))
LOG_PRINTF("Error sending pairing response packet\n");
st->failed_handshakes = 0;
//@ assert 0 <= st->failed_handshakes <= 102;
return 1;
} else { /* keys don't match */
uint8_t response = REPORT_PAIRING_START;
if (send_encrypted_message(st, &response, sizeof(response)))
LOG_PRINTF("Error sending pairing response packet\n");
st->failed_handshakes++;
//@ assert 0 <= st->failed_handshakes <= 102;
//@ assert st->failed_handshakes >= old_failed_handshakes;
return 0;
}
errout:
//@ assert 0 <= st->failed_handshakes <= 102;
//@ assert st->failed_handshakes >= old_failed_handshakes;
return -1;
}
/*@
requires validity: \valid(st) && \valid(usart2_out) && \valid(st->handshake);
requires validity: \valid(st->remote_key + (0..sizeof(st->remote_key)-1));
requires validity: \valid(st->remote_key_reference + (0..sizeof(st->remote_key)-1));
requires validity: \valid(st->handshake_hash + (0..sizeof(st->handshake_hash)-1));
requires sanity: 0 <= st->failed_handshakes < 100;
requires separation: \separated(usart2_out, st, buf, st->handshake);
ensures result: \result \in {0, -1};
ensures state_legal: st->handshake_state \in
{HANDSHAKE_UNINITIALIZED, HANDSHAKE_PHASE1, HANDSHAKE_PHASE2,
HANDSHAKE_DONE_KNOWN_HOST, HANDSHAKE_DONE_UNKNOWN_HOST};
ensures transition_legal:
\result != -1 ==> \old(st->handshake_state) \in {HANDSHAKE_PHASE1, HANDSHAKE_PHASE2};
ensures transition_legal: (\old(st->handshake_state) == HANDSHAKE_PHASE1)
==> st->handshake_state \in {HANDSHAKE_PHASE2, HANDSHAKE_UNINITIALIZED};
ensures transition_legal: (\old(st->handshake_state) == HANDSHAKE_PHASE2)
==> st->handshake_state \in {HANDSHAKE_DONE_KNOWN_HOST, HANDSHAKE_DONE_UNKNOWN_HOST, HANDSHAKE_UNINITIALIZED};
ensures failure_handling: \result == -1 <==> st->handshake_state == HANDSHAKE_UNINITIALIZED;
ensures failure_handling: \result == -1 ==> st->failed_handshakes > \old(st->failed_handshakes);
ensures state_advance_condition: (st->handshake_state == HANDSHAKE_DONE_KNOWN_HOST) ==> key_match_trace == 1;
//assigns *usart2_out, *st, *st->rx_cipher, *st->tx_cipher, *st->handshake;
//assigns key_checked_trace, key_match_trace;
@*/
int try_continue_noise_handshake(struct NoiseState * const st, uint8_t *buf, size_t len) {
//@ ghost key_checked_trace = 0;
//@ ghost key_match_trace = 0;
int rc;
if (!st->handshake) {
LOG_PRINTF("Error: Invalid handshake state\n");
goto errout;
}
/* Run the protocol handshake */
switch (st->handshake_state) {
case HANDSHAKE_PHASE1:
if (handshake_phase1(st, buf, len))
goto errout;
st->handshake_state = HANDSHAKE_PHASE2;
return 0;
case HANDSHAKE_PHASE2:
rc = handshake_phase2(st, buf, len);
if (rc < 0)
goto errout;
if (rc == 1)
uninit_handshake(st, HANDSHAKE_DONE_KNOWN_HOST);
else
uninit_handshake(st, HANDSHAKE_DONE_UNKNOWN_HOST);
break;
default:
LOG_PRINTF("Invalid handshake state\n");
goto errout;
}
return 0;
errout:
uninit_handshake(st, HANDSHAKE_UNINITIALIZED);
st->failed_handshakes++;
LOG_PRINTF("Noise protocol handshake failed, %d failed attempts\n", st->failed_handshakes);
return -1;
}
/*@
requires validity: \valid(st);
requires validity: \valid_read(st->remote_key + (0..sizeof(st->remote_key)-1));
requires validity: \valid(st->remote_key_reference + (0..31));
ensures state: st->handshake_state == HANDSHAKE_DONE_KNOWN_HOST;
assigns st->remote_key_reference[0..31], st->handshake_state;
*/
void persist_remote_key(struct NoiseState *st) {
BLAKE2s_context_t bc;
BLAKE2s_reset(&bc);
fc_BLAKE2s_update_uint8(&bc, st->remote_key, sizeof(st->remote_key));
BLAKE2s_finish(&bc, st->remote_key_reference);
st->handshake_state = HANDSHAKE_DONE_KNOWN_HOST;
}
/*@
requires validity: \valid(st) && \valid(usart2_out) && \valid(st->tx_cipher) && \valid_read(msg + (0..len-1));
requires separation: \separated(usart2_out, st);
ensures length: !(0 <= len <= MAX_HOST_PACKET_SIZE) <==> \result == -3;
ensures \result \in {0, -1, -2, -3};
assigns *st->tx_cipher, *usart2_out;
*/
int send_encrypted_message(struct NoiseState *st, const uint8_t *msg, size_t len) {
int err;
NoiseBuffer noise_buf;
struct {
struct control_packet header;
uint8_t payload[MAX_HOST_PACKET_SIZE];
} pkt;
if (len > sizeof(pkt.payload)) {
LOG_PRINTF("Packet too long\n");
return -3;
}
if (!st->tx_cipher) {
LOG_PRINTF("Cannot send encrypted packet: Data ciphers not yet initialized\n");
return -1;
}
pkt.header.type = HOST_DATA;
fc_memcpy_uint8(pkt.payload, msg, len); /* This is necessary because noises API doesn't support separate in and out buffers. D'oh! */
noise_buffer_set_inout(noise_buf, pkt.payload, len, sizeof(pkt.payload));
HANDLE_NOISE_ERROR(noise_cipherstate_encrypt(st->tx_cipher, &noise_buf), "encrypting data");
send_packet(usart2_out, (uint8_t *)&pkt, noise_buf.size + sizeof(pkt.header));
return 0;
errout:
return -2;
}

56
fw/src/noise.h Normal file
View file

@ -0,0 +1,56 @@
#ifndef __NOISE_H__
#define __NOISE_H__
#include <stdint.h>
#include <noise/protocol.h>
#include "usart_helpers.h"
#include "rand_stm32.h"
#define CURVE25519_KEY_LEN 32
#define MAX_HOST_PACKET_SIZE 128
extern volatile uint8_t host_packet_buf[MAX_HOST_PACKET_SIZE];
extern volatile int host_packet_length;
enum handshake_state {
HANDSHAKE_UNINITIALIZED,
HANDSHAKE_PHASE1,
HANDSHAKE_PHASE2,
HANDSHAKE_DONE_UNKNOWN_HOST,
HANDSHAKE_DONE_KNOWN_HOST,
};
extern volatile enum handshake_state handshake_state;
struct NoiseState {
NoiseHandshakeState *handshake;
enum handshake_state handshake_state;
NoiseCipherState *tx_cipher, *rx_cipher;
uint8_t *local_key;
uint8_t remote_key[CURVE25519_KEY_LEN];
uint8_t *remote_key_reference;
uint8_t handshake_hash[BLAKE2S_HASH_SIZE];
int failed_handshakes;
};
void uninit_handshake(struct NoiseState *st, enum handshake_state new_state);
void noise_state_init(struct NoiseState *st, uint8_t *remote_key_reference, uint8_t *local_key);
void persist_remote_key(struct NoiseState *st);
int start_protocol_handshake(struct NoiseState *st);
int reset_protocol_handshake(struct NoiseState *st);
int generate_identity_key(struct NoiseState *st);
int try_continue_noise_handshake(struct NoiseState * const st, uint8_t *buf, size_t len);
int send_encrypted_message(struct NoiseState *st, const uint8_t *msg, size_t len);
/*@ assigns \nothing; */
void arm_key_scrubber(void);
/*@ assigns \nothing; */
void disarm_key_scrubber(void);
#endif

98
fw/src/packet_interface.c Normal file
View file

@ -0,0 +1,98 @@
#include "packet_interface.h"
#include "noise.h"
#include "cobs.h"
#include "tracing.h"
#include <libopencm3/stm32/usart.h>
#include <libopencm3/stm32/dma.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencmsis/core_cm3.h>
volatile struct {
struct dma_buf dma;
uint8_t data[256];
} usart2_buf = { .dma = { .len = sizeof(usart2_buf.data) } };
struct dma_usart_file usart2_out_s = {
.usart = USART2,
.baudrate = 115200,
.dma = DMA1,
.stream = 6,
.channel = 4,
.irqn = NVIC_DMA_IRQ(1, 6),
.buf = &usart2_buf.dma
};
struct dma_usart_file *usart2_out = &usart2_out_s;
void dma1_stream6_isr(void) {
TRACING_SET(TR_HOST_IF_DMA_IRQ);
static unsigned int fifo_errors = 0; /* debug */
if (dma_get_interrupt_flag(usart2_out->dma, usart2_out->stream, DMA_FEIF)) {
/* Ignore FIFO errors as they're 100% non-critical for UART applications */
dma_clear_interrupt_flags(usart2_out->dma, usart2_out->stream, DMA_FEIF);
fifo_errors++;
TRACING_CLEAR(TR_HOST_IF_DMA_IRQ);
return;
}
/* Transfer complete interrupt */
dma_clear_interrupt_flags(usart2_out->dma, usart2_out->stream, DMA_TCIF);
if (usart2_out->buf->wr_pos != usart2_out->buf->xfr_end) /* buffer not empty */
schedule_dma(usart2_out);
TRACING_CLEAR(TR_HOST_IF_DMA_IRQ);
}
void usart2_isr(void) {
TRACING_SET(TR_HOST_IF_USART_IRQ);
static struct cobs_decode_state host_cobs_state = {0};
if (USART2_SR & USART_SR_ORE) { /* Overrun handling */
LOG_PRINTF("USART2 data register overrun\n");
/* Clear interrupt flag */
(void)USART2_DR; /* FIXME make sure this read is not optimized out */
host_packet_length = -1;
TRACING_CLEAR(TR_HOST_IF_USART_IRQ);
return;
}
uint8_t data = USART2_DR; /* This automatically acknowledges the IRQ */
if (host_packet_length) {
LOG_PRINTF("USART2 COBS buffer overrun\n");
host_packet_length = -1;
TRACING_CLEAR(TR_HOST_IF_USART_IRQ);
return;
}
ssize_t rv = cobs_decode_incremental(&host_cobs_state, (char *)host_packet_buf, sizeof(host_packet_buf), data);
if (rv == 0) {
/* good, empty frame */
LOG_PRINTF("Got empty frame from host\n");
host_packet_length = -1;
} else if (rv == -1) {
/* Decoding frame, wait for next byte */
} else if (rv == -2) {
LOG_PRINTF("Host interface COBS framing error\n");
host_packet_length = -1;
} else if (rv == -3) {
/* invalid empty frame */
LOG_PRINTF("Got double null byte from host\n");
host_packet_length = -1;
} else if (rv == -4) {
/* frame too large */
LOG_PRINTF("Got too large frame from host\n");
host_packet_length = -1;
} else if (rv > 0) {
/* Good, non-empty frame */
host_packet_length = rv;
}
TRACING_CLEAR(TR_HOST_IF_USART_IRQ);
}
void send_packet(struct dma_usart_file *f, const uint8_t *data, size_t len) {
/* ignore return value as putf is blocking and always succeeds */
(void)cobs_encode_incremental(f, putf, (char *)data, len);
flush(f);
}

58
fw/src/packet_interface.h Normal file
View file

@ -0,0 +1,58 @@
#ifndef __PACKET_INTERFACE_H__
#define __PACKET_INTERFACE_H__
#include "usart_helpers.h"
extern struct dma_usart_file *usart2_out;
enum control_packet_types {
_HOST_RESERVED = 0,
HOST_INITIATE_HANDSHAKE = 1,
HOST_HANDSHAKE = 2,
HOST_DATA = 3,
HOST_COMM_ERROR = 4,
HOST_CRYPTO_ERROR = 5,
HOST_TOO_MANY_FAILS = 6,
};
enum packet_types {
_REPORT_RESERVED = 0,
REPORT_KEYBOARD= 1,
REPORT_MOUSE= 2,
REPORT_PAIRING_INPUT = 3,
REPORT_PAIRING_SUCCESS = 4,
REPORT_PAIRING_ERROR = 5,
REPORT_PAIRING_START = 6,
};
struct hid_report_packet {
uint8_t type;
union {
struct {
uint8_t len;
uint8_t report[8];
} report;
struct {
char c;
} pairing_input;
};
} __attribute__((__packed__));
struct control_packet {
uint8_t type;
uint8_t payload[0];
} __attribute__((__packed__));
/*@
requires \valid(f);
requires \valid_read(data + (0..len-1));
requires len > 0;
assigns *f;
*/
void send_packet(struct dma_usart_file *f, const uint8_t *data, size_t len);
#endif

257
fw/src/pgp_wordlist Normal file
View file

@ -0,0 +1,257 @@
Hex Even Word Odd Word
00 aardvark adroitness
01 absurd adviser
02 accrue aftermath
03 acme aggregate
04 adrift alkali
05 adult almighty
06 afflict amulet
07 ahead amusement
08 aimless antenna
09 Algol applicant
0A allow Apollo
0B alone armistice
0C ammo article
0D ancient asteroid
0E apple Atlantic
0F artist atmosphere
10 assume autopsy
11 Athens Babylon
12 atlas backwater
13 Aztec barbecue
14 baboon belowground
15 backfield bifocals
16 backward bodyguard
17 banjo bookseller
18 beaming borderline
19 bedlamp bottomless
1A beehive Bradbury
1B beeswax bravado
1C befriend Brazilian
1D Belfast breakaway
1E berserk Burlington
1F billiard businessman
20 bison butterfat
21 blackjack Camelot
22 blockade candidate
23 blowtorch cannonball
24 bluebird Capricorn
25 bombast caravan
26 bookshelf caretaker
27 brackish celebrate
28 breadline cellulose
29 breakup certify
2A brickyard chambermaid
2B briefcase Cherokee
2C Burbank Chicago
2D button clergyman
2E buzzard coherence
2F cement combustion
30 chairlift commando
31 chatter company
32 checkup component
33 chisel concurrent
34 choking confidence
35 chopper conformist
36 Christmas congregate
37 clamshell consensus
38 classic consulting
39 classroom corporate
3A cleanup corrosion
3B clockwork councilman
3C cobra crossover
3D commence crucifix
3E concert cumbersome
3F cowbell customer
40 crackdown Dakota
41 cranky decadence
42 crowfoot December
43 crucial decimal
44 crumpled designing
45 crusade detector
46 cubic detergent
47 dashboard determine
48 deadbolt dictator
49 deckhand dinosaur
4A dogsled direction
4B dragnet disable
4C drainage disbelief
4D dreadful disruptive
4E drifter distortion
4F dropper document
50 drumbeat embezzle
51 drunken enchanting
52 Dupont enrollment
53 dwelling enterprise
54 eating equation
55 edict equipment
56 egghead escapade
57 eightball Eskimo
58 endorse everyday
59 endow examine
5A enlist existence
5B erase exodus
5C escape fascinate
5D exceed filament
5E eyeglass finicky
5F eyetooth forever
60 facial fortitude
61 fallout frequency
62 flagpole gadgetry
63 flatfoot Galveston
64 flytrap getaway
65 fracture glossary
66 framework gossamer
67 freedom graduate
68 frighten gravity
69 gazelle guitarist
6A Geiger hamburger
6B glitter Hamilton
6C glucose handiwork
6D goggles hazardous
6E goldfish headwaters
6F gremlin hemisphere
70 guidance hesitate
71 hamlet hideaway
72 highchair holiness
73 hockey hurricane
74 indoors hydraulic
75 indulge impartial
76 inverse impetus
77 involve inception
78 island indigo
79 jawbone inertia
7A keyboard infancy
7B kickoff inferno
7C kiwi informant
7D klaxon insincere
7E locale insurgent
7F lockup integrate
80 merit intention
81 minnow inventive
82 miser Istanbul
83 Mohawk Jamaica
84 mural Jupiter
85 music leprosy
86 necklace letterhead
87 Neptune liberty
88 newborn maritime
89 nightbird matchmaker
8A Oakland maverick
8B obtuse Medusa
8C offload megaton
8D optic microscope
8E orca microwave
8F payday midsummer
90 peachy millionaire
91 pheasant miracle
92 physique misnomer
93 playhouse molasses
94 Pluto molecule
95 preclude Montana
96 prefer monument
97 preshrunk mosquito
98 printer narrative
99 prowler nebula
9A pupil newsletter
9B puppy Norwegian
9C python October
9D quadrant Ohio
9E quiver onlooker
9F quota opulent
A0 ragtime Orlando
A1 ratchet outfielder
A2 rebirth Pacific
A3 reform pandemic
A4 regain Pandora
A5 reindeer paperweight
A6 rematch paragon
A7 repay paragraph
A8 retouch paramount
A9 revenge passenger
AA reward pedigree
AB rhythm Pegasus
AC ribcage penetrate
AD ringbolt perceptive
AE robust performance
AF rocker pharmacy
B0 ruffled phonetic
B1 sailboat photograph
B2 sawdust pioneer
B3 scallion pocketful
B4 scenic politeness
B5 scorecard positive
B6 Scotland potato
B7 seabird processor
B8 select provincial
B9 sentence proximate
BA shadow puberty
BB shamrock publisher
BC showgirl pyramid
BD skullcap quantity
BE skydive racketeer
BF slingshot rebellion
C0 slowdown recipe
C1 snapline recover
C2 snapshot repellent
C3 snowcap replica
C4 snowslide reproduce
C5 solo resistor
C6 southward responsive
C7 soybean retraction
C8 spaniel retrieval
C9 spearhead retrospect
CA spellbind revenue
CB spheroid revival
CC spigot revolver
CD spindle sandalwood
CE spyglass sardonic
CF stagehand Saturday
D0 stagnate savagery
D1 stairway scavenger
D2 standard sensation
D3 stapler sociable
D4 steamship souvenir
D5 sterling specialist
D6 stockman speculate
D7 stopwatch stethoscope
D8 stormy stupendous
D9 sugar supportive
DA surmount surrender
DB suspense suspicious
DC sweatband sympathy
DD swelter tambourine
DE tactics telephone
DF talon therapist
E0 tapeworm tobacco
E1 tempest tolerance
E2 tiger tomorrow
E3 tissue torpedo
E4 tonic tradition
E5 topmost travesty
E6 tracker trombonist
E7 transit truncated
E8 trauma typewriter
E9 treadmill ultimate
EA Trojan undaunted
EB trouble underfoot
EC tumor unicorn
ED tunnel unify
EE tycoon universe
EF uncut unravel
F0 unearth upcoming
F1 unwind vacancy
F2 uproot vagabond
F3 upset vertigo
F4 upshot Virginia
F5 vapor visitor
F6 village vocalist
F7 virus voyager
F8 Vulcan warranty
F9 waffle Waterloo
FA wallet whimsical
FB watchword Wichita
FC wayside Wilmington
FD willow Wyoming
FE woodlark yesteryear
FF Zulu Yucatan

135
fw/src/rand_stm32.c Normal file
View file

@ -0,0 +1,135 @@
/* Quick-and-dirty cryptographic RNG based on BLAKE2s
*
* This system uses a 32-byte BLAKE2s hash as internal state seeded by somewhat random post-powerup SRAM content, the
* unique device ID and the program flash contents. This seed state is mixed with values from the hardware RNG for each
* 32-byte block of output data.
*
* The RNG's chaining looks like the following, with H(...) being the BLAKE2s hash function, | being binary
* concatenation and hw_rng(...) being the hardware RNG. c and e are the fixed extraction and chain string constants
* defined below.
*
* Seed: state = H(SRAM | FLASH | hw_rng(64 byte))
*
* Extract: state = H(state | c | hw_rng(64 byte)) block[0] = H(state | e)
* state = H(state | c | hw_rng(64 byte)) block[1] = H(state | e)
* [...]
* state = H(state | c | hw_rng(64 byte)) block[n] = H(state | e)
* state = H(state | c | hw_rng(64 byte))
*
*
* Graphically, with C = H( state | c | rng ) being the chaining function
* and X = H( state | e ) being the extraction function
* this becomes:
*
* rng rng rng rng
* | | | |
* v v v v
* state ---> [C] ---> [C] -- . . . --> [C] ---> [C] ---> new state
* | | |
* v v v
* [X] [X] [X]
* | | |
* v v v
* out[0] out[1] . . . out[n]
*
*/
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <libopencm3/stm32/f4/rng.h>
#include "usart_helpers.h"
#include "rand_stm32.h"
#include "tracing.h"
#include "crypto/noise-c/src/protocol/internal.h"
#include "crypto/noise-c/src/crypto/blake2/blake2s.h"
/* FIXME persist state in backup sram */
extern unsigned _ram_start, _ram_end, _rom_start, _rom_end;
static uint8_t global_stm_rand_state[BLAKE2S_HASH_SIZE];
static uint32_t stm32_read_rng_raw(void) {
if ((RNG_SR & (RNG_SR_SEIS | RNG_SR_CEIS)) || !(RNG_CR & RNG_CR_RNGEN)) {
LOG_PRINTF("RNG error detected, bailing out.\n");
exit(1);
}
while (!(RNG_SR & RNG_SR_DRDY))
;
return RNG_DR;
}
static void rng_seed_blake(BLAKE2s_context_t *bc) {
/* This pulls out 64 bytes. Even though the resulting BLAKE2s hash only is 32 bytes large, the internal state of
* BLAKE2s is larger. Also I don't quite trust the STM32F4's hardware RNG. */
for (int i=0; i<16; i++) {
uint32_t val = stm32_read_rng_raw();
BLAKE2s_update(bc, &val, sizeof(val));
}
}
void rand_init() {
RNG_CR |= RNG_CR_RNGEN;
BLAKE2s_context_t bc;
BLAKE2s_reset(&bc);
/* Seed with entire SRAM area */
BLAKE2s_update(&bc, &_ram_start, &_ram_end - &_ram_start);
/* Seed with entire flash area. This includes the device unique ID if it has not been overwritten. */
BLAKE2s_update(&bc, &_rom_start, &_rom_end - &_rom_start);
/* Seed with 64 bytes of handware RNG input */
rng_seed_blake(&bc);
/* FIXME use ADC to seeed */
BLAKE2s_finish(&bc, global_stm_rand_state);
/* FIXME make sure this is not optimized out */
memset(&bc, 0, sizeof(bc));
}
const char *extraction_constant = "Blake2 RNG extraction constant";
const char *chain_constant = "Blake2 RNG chaining constant";
void noise_rand_bytes(void *bytes, size_t size) {
TRACING_SET(TR_RNG);
BLAKE2s_context_t out_ctx, chain_ctx;
uint8_t *out = (uint8_t *)bytes;
uint8_t hash_buf[BLAKE2S_HASH_SIZE];
for (size_t wr_pos = 0; wr_pos<size; wr_pos += BLAKE2S_HASH_SIZE) {
BLAKE2s_reset(&chain_ctx);
BLAKE2s_update(&chain_ctx, global_stm_rand_state, sizeof(global_stm_rand_state));
BLAKE2s_update(&chain_ctx, chain_constant, strlen(chain_constant));
rng_seed_blake(&chain_ctx);
BLAKE2s_finish(&chain_ctx, global_stm_rand_state);
BLAKE2s_reset(&out_ctx);
BLAKE2s_update(&out_ctx, global_stm_rand_state, sizeof(global_stm_rand_state));
BLAKE2s_update(&out_ctx, extraction_constant, strlen(extraction_constant));
BLAKE2s_finish(&out_ctx, hash_buf);
size_t rem = size-wr_pos;
memcpy(&out[wr_pos], hash_buf, rem < BLAKE2S_HASH_SIZE ? rem : BLAKE2S_HASH_SIZE);
}
BLAKE2s_reset(&chain_ctx);
BLAKE2s_update(&chain_ctx, global_stm_rand_state, sizeof(global_stm_rand_state));
BLAKE2s_update(&chain_ctx, chain_constant, strlen(chain_constant));
rng_seed_blake(&chain_ctx);
BLAKE2s_finish(&chain_ctx, global_stm_rand_state);
/* FIXME make sure this is not optimized out */
memset(&out_ctx, 0, sizeof(out_ctx));
memset(&chain_ctx, 0, sizeof(chain_ctx));
memset(hash_buf, 0, sizeof(hash_buf));
TRACING_CLEAR(TR_RNG);
}
#ifdef ED25519_CUSTOMRANDOM /* We are building against ed25519-donna, which needs a random function */
void ed25519_randombytes_unsafe(void *p, size_t len) {
noise_rand_bytes(p, len);
}
#endif

11
fw/src/rand_stm32.h Normal file
View file

@ -0,0 +1,11 @@
#ifndef __RAND_STM32_H__
#define __RAND_STM32_H__
#include <stdint.h>
#include <unistd.h>
#define BLAKE2S_HASH_SIZE 32
void rand_init(void);
#endif

523
fw/src/tinyprintf.c Normal file
View file

@ -0,0 +1,523 @@
/*
File: tinyprintf.c
Copyright (C) 2004 Kustaa Nyholm
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "tinyprintf.h"
/*
* Configuration
*/
/* Enable long int support */
#define PRINTF_LONG_SUPPORT
/* Enable long long int support (implies long int support) */
#define PRINTF_LONG_LONG_SUPPORT
/* Enable %z (size_t) support */
#define PRINTF_SIZE_T_SUPPORT
/*
* Configuration adjustments
*/
#ifdef PRINTF_SIZE_T_SUPPORT
#include <sys/types.h>
#endif
#ifdef PRINTF_LONG_LONG_SUPPORT
# define PRINTF_LONG_SUPPORT
#endif
/* __SIZEOF_<type>__ defined at least by gcc */
#ifdef __SIZEOF_POINTER__
# define SIZEOF_POINTER __SIZEOF_POINTER__
#endif
#ifdef __SIZEOF_LONG_LONG__
# define SIZEOF_LONG_LONG __SIZEOF_LONG_LONG__
#endif
#ifdef __SIZEOF_LONG__
# define SIZEOF_LONG __SIZEOF_LONG__
#endif
#ifdef __SIZEOF_INT__
# define SIZEOF_INT __SIZEOF_INT__
#endif
#ifdef __GNUC__
# define _TFP_GCC_NO_INLINE_ __attribute__ ((noinline))
#else
# define _TFP_GCC_NO_INLINE_
#endif
/*
* Implementation
*/
struct param {
char lz:1; /**< Leading zeros */
char alt:1; /**< alternate form */
char uc:1; /**< Upper case (for base16 only) */
char align_left:1; /**< 0 == align right (default), 1 == align left */
unsigned int width; /**< field width */
char sign; /**< The sign to display (if any) */
unsigned int base; /**< number base (e.g.: 8, 10, 16) */
char *bf; /**< Buffer to output */
};
#ifdef PRINTF_LONG_LONG_SUPPORT
static void _TFP_GCC_NO_INLINE_ ulli2a(
unsigned long long int num, struct param *p)
{
int n = 0;
unsigned long long int d = 1;
char *bf = p->bf;
while (num / d >= p->base)
d *= p->base;
while (d != 0) {
int dgt = num / d;
num %= d;
d /= p->base;
if (n || dgt > 0 || d == 0) {
*bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10);
++n;
}
}
*bf = 0;
}
static void lli2a(long long int num, struct param *p)
{
if (num < 0) {
num = -num;
p->sign = '-';
}
ulli2a(num, p);
}
#endif
#ifdef PRINTF_LONG_SUPPORT
static void uli2a(unsigned long int num, struct param *p)
{
int n = 0;
unsigned long int d = 1;
char *bf = p->bf;
while (num / d >= p->base)
d *= p->base;
while (d != 0) {
int dgt = num / d;
num %= d;
d /= p->base;
if (n || dgt > 0 || d == 0) {
*bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10);
++n;
}
}
*bf = 0;
}
static void li2a(long num, struct param *p)
{
if (num < 0) {
num = -num;
p->sign = '-';
}
uli2a(num, p);
}
#endif
static void ui2a(unsigned int num, struct param *p)
{
int n = 0;
unsigned int d = 1;
char *bf = p->bf;
while (num / d >= p->base)
d *= p->base;
while (d != 0) {
int dgt = num / d;
num %= d;
d /= p->base;
if (n || dgt > 0 || d == 0) {
*bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10);
++n;
}
}
*bf = 0;
}
static void i2a(int num, struct param *p)
{
if (num < 0) {
num = -num;
p->sign = '-';
}
ui2a(num, p);
}
static int a2d(char ch)
{
if (ch >= '0' && ch <= '9')
return ch - '0';
else if (ch >= 'a' && ch <= 'f')
return ch - 'a' + 10;
else if (ch >= 'A' && ch <= 'F')
return ch - 'A' + 10;
else
return -1;
}
static char a2u(char ch, const char **src, int base, unsigned int *nump)
{
const char *p = *src;
unsigned int num = 0;
int digit;
while ((digit = a2d(ch)) >= 0) {
if (digit > base)
break;
num = num * base + digit;
ch = *p++;
}
*src = p;
*nump = num;
return ch;
}
static void putchw(void *putp, putcf putf, struct param *p)
{
char ch;
int n = p->width;
char *bf = p->bf;
/* Number of filling characters */
while (*bf++ && n > 0)
n--;
if (p->sign)
n--;
if (p->alt && p->base == 16)
n -= 2;
else if (p->alt && p->base == 8)
n--;
/* Fill with space to align to the right, before alternate or sign */
if (!p->lz && !p->align_left) {
while (n-- > 0)
putf(putp, ' ');
}
/* print sign */
if (p->sign)
putf(putp, p->sign);
/* Alternate */
if (p->alt && p->base == 16) {
putf(putp, '0');
putf(putp, (p->uc ? 'X' : 'x'));
} else if (p->alt && p->base == 8) {
putf(putp, '0');
}
/* Fill with zeros, after alternate or sign */
if (p->lz) {
while (n-- > 0)
putf(putp, '0');
}
/* Put actual buffer */
bf = p->bf;
while ((ch = *bf++))
putf(putp, ch);
/* Fill with space to align to the left, after string */
if (!p->lz && p->align_left) {
while (n-- > 0)
putf(putp, ' ');
}
}
void tfp_format(void *putp, putcf putf, const char *fmt, va_list va)
{
struct param p;
#ifdef PRINTF_LONG_SUPPORT
char bf[23]; /* long = 64b on some architectures */
#else
char bf[12]; /* int = 32b on some architectures */
#endif
char ch;
p.bf = bf;
while ((ch = *(fmt++))) {
if (ch != '%') {
putf(putp, ch);
} else {
#ifdef PRINTF_LONG_SUPPORT
char lng = 0; /* 1 for long, 2 for long long */
#endif
/* Init parameter struct */
p.lz = 0;
p.alt = 0;
p.width = 0;
p.align_left = 0;
p.sign = 0;
/* Flags */
while ((ch = *(fmt++))) {
switch (ch) {
case '-':
p.align_left = 1;
continue;
case '0':
p.lz = 1;
continue;
case '#':
p.alt = 1;
continue;
default:
break;
}
break;
}
/* Width */
if (ch >= '0' && ch <= '9') {
ch = a2u(ch, &fmt, 10, &(p.width));
}
/* We accept 'x.y' format but don't support it completely:
* we ignore the 'y' digit => this ignores 0-fill
* size and makes it == width (ie. 'x') */
if (ch == '.') {
p.lz = 1; /* zero-padding */
/* ignore actual 0-fill size: */
do {
ch = *(fmt++);
} while ((ch >= '0') && (ch <= '9'));
}
#ifdef PRINTF_SIZE_T_SUPPORT
# ifdef PRINTF_LONG_SUPPORT
if (ch == 'z') {
ch = *(fmt++);
if (sizeof(size_t) == sizeof(unsigned long int))
lng = 1;
# ifdef PRINTF_LONG_LONG_SUPPORT
else if (sizeof(size_t) == sizeof(unsigned long long int))
lng = 2;
# endif
} else
# endif
#endif
#ifdef PRINTF_LONG_SUPPORT
if (ch == 'l') {
ch = *(fmt++);
lng = 1;
#ifdef PRINTF_LONG_LONG_SUPPORT
if (ch == 'l') {
ch = *(fmt++);
lng = 2;
}
#endif
}
#endif
switch (ch) {
case 0:
goto abort;
case 'u':
p.base = 10;
#ifdef PRINTF_LONG_SUPPORT
#ifdef PRINTF_LONG_LONG_SUPPORT
if (2 == lng)
ulli2a(va_arg(va, unsigned long long int), &p);
else
#endif
if (1 == lng)
uli2a(va_arg(va, unsigned long int), &p);
else
#endif
ui2a(va_arg(va, unsigned int), &p);
putchw(putp, putf, &p);
break;
case 'd':
case 'i':
p.base = 10;
#ifdef PRINTF_LONG_SUPPORT
#ifdef PRINTF_LONG_LONG_SUPPORT
if (2 == lng)
lli2a(va_arg(va, long long int), &p);
else
#endif
if (1 == lng)
li2a(va_arg(va, long int), &p);
else
#endif
i2a(va_arg(va, int), &p);
putchw(putp, putf, &p);
break;
#ifdef SIZEOF_POINTER
case 'p':
p.alt = 1;
# if defined(SIZEOF_INT) && SIZEOF_POINTER <= SIZEOF_INT
lng = 0;
# elif defined(SIZEOF_LONG) && SIZEOF_POINTER <= SIZEOF_LONG
lng = 1;
# elif defined(SIZEOF_LONG_LONG) && SIZEOF_POINTER <= SIZEOF_LONG_LONG
lng = 2;
# endif
#endif
__attribute__((fallthrough));
case 'x':
__attribute__((fallthrough));
case 'X':
p.base = 16;
p.uc = (ch == 'X')?1:0;
#ifdef PRINTF_LONG_SUPPORT
#ifdef PRINTF_LONG_LONG_SUPPORT
if (2 == lng)
ulli2a(va_arg(va, unsigned long long int), &p);
else
#endif
if (1 == lng)
uli2a(va_arg(va, unsigned long int), &p);
else
#endif
ui2a(va_arg(va, unsigned int), &p);
putchw(putp, putf, &p);
break;
case 'o':
p.base = 8;
ui2a(va_arg(va, unsigned int), &p);
putchw(putp, putf, &p);
break;
case 'c':
putf(putp, (char)(va_arg(va, int)));
break;
case 's':
p.bf = va_arg(va, char *);
putchw(putp, putf, &p);
p.bf = bf;
break;
case '%':
putf(putp, ch);
default:
break;
}
}
}
abort:;
}
#if TINYPRINTF_DEFINE_TFP_PRINTF
static putcf stdout_putf;
static void *stdout_putp;
void init_printf(void *putp, putcf putf)
{
stdout_putf = putf;
stdout_putp = putp;
}
void tfp_printf(char *fmt, ...)
{
va_list va;
va_start(va, fmt);
tfp_format(stdout_putp, stdout_putf, fmt, va);
va_end(va);
}
#endif
#if TINYPRINTF_DEFINE_TFP_SPRINTF
struct _vsnprintf_putcf_data
{
size_t dest_capacity;
char *dest;
size_t num_chars;
};
static void _vsnprintf_putcf(void *p, char c)
{
struct _vsnprintf_putcf_data *data = (struct _vsnprintf_putcf_data*)p;
if (data->num_chars < data->dest_capacity)
data->dest[data->num_chars] = c;
data->num_chars ++;
}
int tfp_vsnprintf(char *str, size_t size, const char *format, va_list ap)
{
struct _vsnprintf_putcf_data data;
if (size < 1)
return 0;
data.dest = str;
data.dest_capacity = size-1;
data.num_chars = 0;
tfp_format(&data, _vsnprintf_putcf, format, ap);
if (data.num_chars < data.dest_capacity)
data.dest[data.num_chars] = '\0';
else
data.dest[data.dest_capacity] = '\0';
return data.num_chars;
}
int tfp_snprintf(char *str, size_t size, const char *format, ...)
{
va_list ap;
int retval;
va_start(ap, format);
retval = tfp_vsnprintf(str, size, format, ap);
va_end(ap);
return retval;
}
struct _vsprintf_putcf_data
{
char *dest;
size_t num_chars;
};
static void _vsprintf_putcf(void *p, char c)
{
struct _vsprintf_putcf_data *data = (struct _vsprintf_putcf_data*)p;
data->dest[data->num_chars++] = c;
}
int tfp_vsprintf(char *str, const char *format, va_list ap)
{
struct _vsprintf_putcf_data data;
data.dest = str;
data.num_chars = 0;
tfp_format(&data, _vsprintf_putcf, format, ap);
data.dest[data.num_chars] = '\0';
return data.num_chars;
}
int tfp_sprintf(char *str, const char *format, ...)
{
va_list ap;
int retval;
va_start(ap, format);
retval = tfp_vsprintf(str, format, ap);
va_end(ap);
return retval;
}
#endif

186
fw/src/tinyprintf.h Normal file
View file

@ -0,0 +1,186 @@
/*
File: tinyprintf.h
Copyright (C) 2004 Kustaa Nyholm
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
This library is really just two files: 'tinyprintf.h' and 'tinyprintf.c'.
They provide a simple and small (+400 loc) printf functionality to
be used in embedded systems.
I've found them so useful in debugging that I do not bother with a
debugger at all.
They are distributed in source form, so to use them, just compile them
into your project.
Two printf variants are provided: printf and the 'sprintf' family of
functions ('snprintf', 'sprintf', 'vsnprintf', 'vsprintf').
The formats supported by this implementation are:
'c' 'd' 'i' 'o' 'p' 'u' 's' 'x' 'X'.
Zero padding and field width are also supported.
If the library is compiled with 'PRINTF_SUPPORT_LONG' defined, then
the long specifier is also supported. Note that this will pull in some
long math routines (pun intended!) and thus make your executable
noticeably longer. Likewise with 'PRINTF_LONG_LONG_SUPPORT' for the
long long specifier, and with 'PRINTF_SIZE_T_SUPPORT' for the size_t
specifier.
The memory footprint of course depends on the target CPU, compiler and
compiler options, but a rough guesstimate (based on a H8S target) is about
1.4 kB for code and some twenty 'int's and 'char's, say 60 bytes of stack space.
Not too bad. Your mileage may vary. By hacking the source code you can
get rid of some hundred bytes, I'm sure, but personally I feel the balance of
functionality and flexibility versus code size is close to optimal for
many embedded systems.
To use the printf, you need to supply your own character output function,
something like :
void putc ( void* p, char c)
{
while (!SERIAL_PORT_EMPTY) ;
SERIAL_PORT_TX_REGISTER = c;
}
Before you can call printf, you need to initialize it to use your
character output function with something like:
init_printf(NULL,putc);
Notice the 'NULL' in 'init_printf' and the parameter 'void* p' in 'putc',
the NULL (or any pointer) you pass into the 'init_printf' will eventually be
passed to your 'putc' routine. This allows you to pass some storage space (or
anything really) to the character output function, if necessary.
This is not often needed but it was implemented like that because it made
implementing the sprintf function so neat (look at the source code).
The code is re-entrant, except for the 'init_printf' function, so it is safe
to call it from interrupts too, although this may result in mixed output.
If you rely on re-entrancy, take care that your 'putc' function is re-entrant!
The printf and sprintf functions are actually macros that translate to
'tfp_printf' and 'tfp_sprintf' when 'TINYPRINTF_OVERRIDE_LIBC' is set
(default). Setting it to 0 makes it possible to use them along with
'stdio.h' printf's in a single source file. When
'TINYPRINTF_OVERRIDE_LIBC' is set, please note that printf/sprintf are
not function-like macros, so if you have variables or struct members
with these names, things will explode in your face. Without variadic
macros this is the best we can do to wrap these function. If it is a
problem, just give up the macros and use the functions directly, or
rename them.
It is also possible to avoid defining tfp_printf and/or tfp_sprintf by
clearing 'TINYPRINTF_DEFINE_TFP_PRINTF' and/or
'TINYPRINTF_DEFINE_TFP_SPRINTF' to 0. This allows for example to
export only tfp_format, which is at the core of all the other
functions.
For further details see source code.
regs Kusti, 23.10.2004
*/
#ifndef __TFP_PRINTF__
#define __TFP_PRINTF__
#include <stdarg.h>
/* Global configuration */
/* Set this to 0 if you do not want to provide tfp_printf */
#ifndef TINYPRINTF_DEFINE_TFP_PRINTF
# define TINYPRINTF_DEFINE_TFP_PRINTF 1
#endif
/* Set this to 0 if you do not want to provide
tfp_sprintf/snprintf/vsprintf/vsnprintf */
#ifndef TINYPRINTF_DEFINE_TFP_SPRINTF
# define TINYPRINTF_DEFINE_TFP_SPRINTF 1
#endif
/* Set this to 0 if you do not want tfp_printf and
tfp_{vsn,sn,vs,s}printf to be also available as
printf/{vsn,sn,vs,s}printf */
#ifndef TINYPRINTF_OVERRIDE_LIBC
# define TINYPRINTF_OVERRIDE_LIBC 1
#endif
/* Optional external types dependencies */
#if TINYPRINTF_DEFINE_TFP_SPRINTF
# include <sys/types.h> /* size_t */
#endif
/* Declarations */
#ifdef __GNUC__
# define _TFP_SPECIFY_PRINTF_FMT(fmt_idx,arg1_idx) \
__attribute__((format (printf, fmt_idx, arg1_idx)))
#else
# define _TFP_SPECIFY_PRINTF_FMT(fmt_idx,arg1_idx)
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*putcf) (void *, char);
/*
'tfp_format' really is the central function for all tinyprintf. For
each output character after formatting, the 'putf' callback is
called with 2 args:
- an arbitrary void* 'putp' param defined by the user and
passed unmodified from 'tfp_format',
- the character.
The 'tfp_printf' and 'tfp_sprintf' functions simply define their own
callback and pass to it the right 'putp' it is expecting.
*/
void tfp_format(void *putp, putcf putf, const char *fmt, va_list va);
#if TINYPRINTF_DEFINE_TFP_SPRINTF
int tfp_vsnprintf(char *str, size_t size, const char *fmt, va_list ap);
int tfp_snprintf(char *str, size_t size, const char *fmt, ...) \
_TFP_SPECIFY_PRINTF_FMT(3, 4);
int tfp_vsprintf(char *str, const char *fmt, va_list ap);
int tfp_sprintf(char *str, const char *fmt, ...) \
_TFP_SPECIFY_PRINTF_FMT(2, 3);
# if TINYPRINTF_OVERRIDE_LIBC
# define vsnprintf tfp_vsnprintf
# define snprintf tfp_snprintf
# define vsprintf tfp_vsprintf
# define sprintf tfp_sprintf
# endif
#endif
#if TINYPRINTF_DEFINE_TFP_PRINTF
void init_printf(void *putp, putcf putf);
void tfp_printf(char *fmt, ...) _TFP_SPECIFY_PRINTF_FMT(1, 2);
# if TINYPRINTF_OVERRIDE_LIBC
# define printf tfp_printf
# endif
#endif
#ifdef __cplusplus
}
#endif
#endif

25
fw/src/tracing.h Normal file
View file

@ -0,0 +1,25 @@
#ifndef __TRACING_H__
#define __TRACING_H__
#include <libopencm3/stm32/gpio.h>
#ifndef VERIFICATION
#define TRACING_SET(i) gpio_set(GPIOD, (1<<i))
#define TRACING_CLEAR(i) gpio_clear(GPIOD, (1<<i))
#else
#define TRACING_SET(i) ((void)0)
#define TRACING_CLEAR(i) ((void)0)
#endif
enum tracing_channels {
TR_HID_MESSAGE_HANDLER = 0,
TR_DEBUG_OUT_DMA_IRQ = 1,
TR_HOST_IF_DMA_IRQ = 2,
TR_HOST_IF_USART_IRQ = 3,
TR_USBH_POLL = 4,
TR_HOST_PKT_HANDLER = 5,
TR_NOISE_HANDSHAKE = 6,
TR_RNG = 7,
};
#endif

149
fw/src/usart_helpers.c Normal file
View file

@ -0,0 +1,149 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2015 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "usart_helpers.h"
#define TINYPRINTF_OVERRIDE_LIBC 0
#define TINYPRINTF_DEFINE_TFP_SPRINTF 0
#include "tinyprintf.h"
#include "cobs.h"
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/stm32/dma.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencmsis/core_cm3.h>
void usart_fprintf(struct dma_usart_file *f, const char *str, ...) {
va_list va;
va_start(va, str);
tfp_format(f, (void (*)(void *, char c))putf, str, va);
va_end(va);
flush(f);
}
void usart_init(uint32_t arg_usart, uint32_t baudrate) {
usart_set_baudrate(arg_usart, baudrate);
usart_set_databits(arg_usart, 8);
usart_set_flow_control(arg_usart, USART_FLOWCONTROL_NONE);
usart_set_mode(arg_usart, USART_MODE_TX | USART_MODE_RX);
usart_set_parity(arg_usart, USART_PARITY_NONE);
usart_set_stopbits(arg_usart, USART_STOPBITS_1);
usart_enable(arg_usart);
}
void usart_dma_init(struct dma_usart_file *f) {
usart_init(f->usart, f->baudrate);
f->buf->xfr_start = -1,
f->buf->xfr_end = 0,
f->buf->wr_pos = 0,
dma_stream_reset(f->dma, f->stream);
dma_channel_select(f->dma, f->stream, DMA_SxCR_CHSEL(f->channel));
dma_set_peripheral_address(f->dma, f->stream, (uint32_t)&USART_DR(f->usart));
dma_set_transfer_mode(f->dma, f->stream, DMA_SxCR_DIR_MEM_TO_PERIPHERAL);
dma_enable_memory_increment_mode(f->dma, f->stream);
dma_set_peripheral_size(f->dma, f->stream, DMA_SxCR_PSIZE_8BIT);
dma_set_memory_size(f->dma, f->stream, DMA_SxCR_MSIZE_8BIT);
dma_set_priority(f->dma, f->stream, DMA_SxCR_PL_VERY_HIGH);
dma_enable_transfer_complete_interrupt(f->dma, f->stream);
dma_enable_fifo_error_interrupt(f->dma, f->stream);
usart_enable_tx_dma(f->usart);
}
void usart_kickoff_dma(uint32_t dma, uint8_t stream, volatile uint8_t *buf, size_t len) {
/* initiate transmission of new buffer */
dma_set_memory_address(dma, stream, (uint32_t)buf); /* select active buffer address */
dma_set_number_of_data(dma, stream, len);
dma_enable_stream(dma, stream);
}
void schedule_dma(volatile struct dma_usart_file *f) {
volatile struct dma_buf *buf = f->buf;
uint32_t xfr_len, xfr_start = buf->xfr_end;
if (buf->wr_pos > xfr_start) /* no wraparound */
xfr_len = buf->wr_pos - xfr_start;
else /* wraparound */
xfr_len = buf->len - xfr_start; /* schedule transfer until end of buffer */
buf->xfr_start = xfr_start;
buf->xfr_end = (xfr_start + xfr_len) % buf->len; /* handle wraparound */
usart_kickoff_dma(f->dma, f->stream, buf->data + xfr_start, xfr_len);
}
int dma_fifo_push(volatile struct dma_buf *buf, char c) {
if (buf->wr_pos == buf->xfr_start)
return -EBUSY;
buf->data[buf->wr_pos] = c;
buf->wr_pos = (buf->wr_pos + 1) % buf->len;
return 0;
}
int putf(void *file, char c) {
volatile struct dma_usart_file *f = (struct dma_usart_file *)file;
nvic_disable_irq(f->irqn);
/* push char to fifo, busy-loop if stalled to wait for USART to empty fifo via DMA */
while (dma_fifo_push(f->buf, c) == -EBUSY) {
nvic_enable_irq(f->irqn);
flush(f);
nvic_disable_irq(f->irqn);
}
nvic_enable_irq(f->irqn);
return 0;
}
int putb(void *file, const uint8_t *buf, size_t len) {
volatile struct dma_usart_file *f = (struct dma_usart_file *)file;
nvic_disable_irq(f->irqn);
for (size_t i=0; i<len; i++) {
/* push char to fifo, busy-loop if stalled to wait for USART to empty fifo via DMA */
while (dma_fifo_push(f->buf, buf[i]) == -EBUSY) {
nvic_enable_irq(f->irqn);
nvic_disable_irq(f->irqn);
}
}
nvic_enable_irq(f->irqn);
return 0;
}
void flush(void *file) {
volatile struct dma_usart_file *f = (struct dma_usart_file *)file;
nvic_disable_irq(f->irqn);
/* If the DMA stream is idle right now, schedule a transfer */
if (!(DMA_SCR(f->dma, f->stream) & DMA_SxCR_EN)) { /* DMA is not running */
//&& !dma_get_interrupt_flag(f->dma, f->stream, DMA_TCIF)/* DMA interrupt is clear */) {
dma_clear_interrupt_flags(f->dma, f->stream, DMA_TCIF);
schedule_dma(f);
}
nvic_enable_irq(f->irqn);
}

88
fw/src/usart_helpers.h Normal file
View file

@ -0,0 +1,88 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2015 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef USBH_USART_HELPERS_H
#define USBH_USART_HELPERS_H
#include "usbh_core.h"
#include <stdint.h>
#include <stdarg.h>
#include <errno.h>
BEGIN_DECLS
struct dma_buf {
uint32_t xfr_start; /* Start index of running DMA transfer */
uint32_t xfr_end; /* End index of running DMA transfer plus one */
uint32_t wr_pos; /* Next index to be written */
uint32_t len;
uint8_t data[0];
};
struct dma_usart_file {
uint32_t usart;
uint32_t baudrate;
uint32_t dma;
uint8_t stream;
uint8_t channel;
uint8_t irqn;
volatile struct dma_buf *buf;
};
extern struct dma_usart_file *debug_out;
void usart_init(uint32_t usart, uint32_t baudrate);
void usart_fprintf(struct dma_usart_file *f, const char *str, ...);
void usart_fifo_push(uint8_t c);
void usart_dma_init(struct dma_usart_file *f);
void usart_kickoff_dma(uint32_t dma, uint8_t stream, volatile uint8_t *buf, size_t len);
void schedule_dma(volatile struct dma_usart_file *f);
int dma_fifo_push(volatile struct dma_buf *buf, char c);
int putf(void *file, char c);
int putb(void *file, const uint8_t *buf, size_t len);
void flush(void *file);
/* This macro abomination templates a bunch of dma-specific register/constant names from preprocessor macros passed in
* from cmake. */
#define DMA_PASTE(num) DMA ## num
#define DMA(num) DMA_PASTE(num)
#define NVIC_DMA_IRQ_PASTE(dma, stream) NVIC_ ## DMA ## dma ## _ ## STREAM ## stream ## _IRQ
#define NVIC_DMA_IRQ(dma, stream) NVIC_DMA_IRQ_PASTE(dma, stream)
#define DMA_ISR_PASTE(dma, stream) DMA ## dma ## _ ## STREAM ## stream ## _IRQHandler
#define DMA_ISR(dma, stream) DMA_ISR_PASTE(dma, stream)
#ifdef USART_DEBUG
#define LOG_PRINTF(format, ...) usart_fprintf(debug_out, format, ##__VA_ARGS__);
#else
#define LOG_PRINTF(dummy, ...) ((void)dummy)
#endif
#define UNUSED(var) ((void)var)
END_DECLS
#endif

726
fw/src/usbh_core.c Normal file
View file

@ -0,0 +1,726 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2015 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "usbh_config.h"
#include "usbh_lld_stm32f4.h"
#include "driver/usbh_device_driver.h"
#include "usart_helpers.h"
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/usb/usbstd.h>
#include <stddef.h>
static struct {
bool enumeration_run;
const usbh_low_level_driver_t * const *lld_drivers;
const usbh_dev_driver_t * const *dev_drivers;
int8_t address_temporary;
} usbh_data = {};
static void set_enumeration(void)
{
usbh_data.enumeration_run = true;
}
static void reset_enumeration(void)
{
usbh_data.enumeration_run = false;
}
static bool enumeration(void)
{
return usbh_data.enumeration_run;
}
void device_remove(usbh_device_t *dev)
{
if (dev->drv && dev->drvdata) {
dev->drv->remove(dev->drvdata);
}
dev->address = -1;
dev->drv = NULL;
dev->drvdata = NULL;
}
/**
*
*/
static bool find_driver(usbh_device_t *dev, const usbh_dev_driver_info_t * device_info)
{
#define CHECK_PARTIAL_COMPATIBILITY(what) \
if (usbh_data.dev_drivers[i]->info->what != -1\
&& device_info->what != usbh_data.dev_drivers[i]->info->what) {\
i++;\
continue;\
}
int i = 0;
while (usbh_data.dev_drivers[i]) {
CHECK_PARTIAL_COMPATIBILITY(ifaceClass);
CHECK_PARTIAL_COMPATIBILITY(ifaceSubClass);
CHECK_PARTIAL_COMPATIBILITY(ifaceProtocol);
CHECK_PARTIAL_COMPATIBILITY(deviceClass);
CHECK_PARTIAL_COMPATIBILITY(deviceSubClass);
CHECK_PARTIAL_COMPATIBILITY(deviceProtocol);
CHECK_PARTIAL_COMPATIBILITY(idVendor);
CHECK_PARTIAL_COMPATIBILITY(idProduct);
dev->drv = usbh_data.dev_drivers[i];
dev->drvdata = dev->drv->init(dev);
if (!dev->drvdata) {
LOG_PRINTF("Unable to initialize device driver at index %d\n", i);
i++;
continue;
}
return true;
}
return false;
#undef CHECK_PARTIAL_COMPATIBILITY
}
static void device_register(void *descriptors, uint16_t descriptors_len, usbh_device_t *dev)
{
uint32_t i = 0;
uint8_t *buf = (uint8_t *)descriptors;
dev->drv = NULL;
dev->drvdata = NULL;
uint8_t desc_len = buf[i];
uint8_t desc_type = buf[i + 1];
usbh_dev_driver_info_t device_info;
if (desc_type == USB_DT_DEVICE) {
struct usb_device_descriptor *device_desc = (void*)&buf[i];
LOG_PRINTF("DEVICE DESCRIPTOR\n");
device_info.deviceClass = device_desc->bDeviceClass;
device_info.deviceSubClass = device_desc->bDeviceSubClass;
device_info.deviceProtocol = device_desc->bDeviceProtocol;
device_info.idVendor = device_desc->idVendor;
device_info.idProduct = device_desc->idProduct;
} else {
LOG_PRINTF("INVALID descriptors pointer - fatal error");
return;
}
while (i < descriptors_len) {
desc_len = buf[i];
desc_type = buf[i + 1];
switch (desc_type) {
case USB_DT_INTERFACE:
{
LOG_PRINTF("INTERFACE_DESCRIPTOR\n");
struct usb_interface_descriptor *iface = (void*)&buf[i];
device_info.ifaceClass = iface->bInterfaceClass;
device_info.ifaceSubClass = iface->bInterfaceSubClass;
device_info.ifaceProtocol = iface->bInterfaceProtocol;
if (find_driver(dev, &device_info)) {
int k = 0;
while (k < descriptors_len) {
desc_len = buf[k];
void *drvdata = dev->drvdata;
LOG_PRINTF("[%d]", buf[k+1]);
if (dev->drv->analyze_descriptor(drvdata, &buf[k])) {
LOG_PRINTF("Device Initialized\n");
return;
}
if (desc_len == 0) {
LOG_PRINTF("Problem occured while parsing complete configuration descriptor");
return;
}
k += desc_len;
}
LOG_PRINTF("Device driver isn't compatible with this device\n");
device_remove(dev);
} else {
LOG_PRINTF("No compatible driver has been found for interface #%d\n", iface->bInterfaceNumber);
}
}
break;
default:
break;
}
if (desc_len == 0) {
LOG_PRINTF("PROBLEM WITH PARSE %d\n",i);
return;
}
i += desc_len;
}
LOG_PRINTF("Device NOT Initialized\n");
}
void usbh_init(const usbh_low_level_driver_t * const low_level_drivers[], const usbh_dev_driver_t * const device_drivers[])
{
if (!low_level_drivers) {
return;
}
usbh_data.lld_drivers = (const usbh_low_level_driver_t **)low_level_drivers;
usbh_data.dev_drivers = device_drivers;
uint32_t k = 0;
while (usbh_data.lld_drivers[k]) {
LOG_PRINTF("Initialization low-level driver with index=%d\n", k);
usbh_device_t * usbh_device =
((usbh_generic_data_t *)(usbh_data.lld_drivers[k])->driver_data)->usbh_device;
uint32_t i;
for (i = 0; i < USBH_MAX_DEVICES; i++) {
//~ LOG_PRINTF("%p ", &usbh_device[i]);
usbh_device[i].address = -1;
usbh_device[i].drv = 0;
usbh_device[i].drvdata = 0;
}
usbh_data.lld_drivers[k]->init(usbh_data.lld_drivers[k]->driver_data);
k++;
}
}
static void device_xfer_control_write_setup(const void *data, uint16_t datalen, usbh_packet_callback_t callback, usbh_device_t *dev)
{
usbh_packet_t packet;
packet.data.out = data;
packet.datalen = datalen;
packet.address = dev->address;
packet.endpoint_address = 0;
packet.endpoint_size_max = dev->packet_size_max0;
packet.endpoint_type = USBH_ENDPOINT_TYPE_CONTROL;
packet.control_type = USBH_CONTROL_TYPE_SETUP;
packet.speed = dev->speed;
packet.callback = callback;
packet.callback_arg = dev;
packet.toggle = &dev->toggle0;
usbh_write(dev, &packet);
LOG_PRINTF("WR-setup@device...%d \n", dev->address);
}
static void device_xfer_control_write_data(const void *data, uint16_t datalen, usbh_packet_callback_t callback, usbh_device_t *dev)
{
usbh_packet_t packet;
packet.data.out = data;
packet.datalen = datalen;
packet.address = dev->address;
packet.endpoint_address = 0;
packet.endpoint_size_max = dev->packet_size_max0;
packet.endpoint_type = USBH_ENDPOINT_TYPE_CONTROL;
packet.control_type = USBH_CONTROL_TYPE_DATA;
packet.speed = dev->speed;
packet.callback = callback;
packet.callback_arg = dev;
packet.toggle = &dev->toggle0;
usbh_write(dev, &packet);
LOG_PRINTF("WR-data@device...%d \n", dev->address);
}
static void device_xfer_control_read(void *data, uint16_t datalen, usbh_packet_callback_t callback, usbh_device_t *dev)
{
usbh_packet_t packet;
packet.data.in = data;
packet.datalen = datalen;
packet.address = dev->address;
packet.endpoint_address = 0;
packet.endpoint_size_max = dev->packet_size_max0;
packet.endpoint_type = USBH_ENDPOINT_TYPE_CONTROL;
packet.speed = dev->speed;
packet.callback = callback;
packet.callback_arg = dev;
packet.toggle = &dev->toggle0;
usbh_read(dev, &packet);
LOG_PRINTF("RD@device...%d | \n", dev->address);
}
static void control_state_machine(usbh_device_t *dev, usbh_packet_callback_data_t cb_data)
{
switch (dev->control.state) {
case USBH_CONTROL_STATE_SETUP:
if (cb_data.status != USBH_PACKET_CALLBACK_STATUS_OK) {
dev->control.state = USBH_CONTROL_STATE_NONE;
// Unable to deliver setup control packet - this is a fatal error
usbh_packet_callback_data_t ret_data;
ret_data.status = USBH_PACKET_CALLBACK_STATUS_EFATAL;
ret_data.transferred_length = 0;
dev->control.callback(dev, ret_data);
break;
}
if (dev->control.setup_data.bmRequestType & USB_REQ_TYPE_IN) {
dev->control.state = USBH_CONTROL_STATE_DATA;
device_xfer_control_read(dev->control.data.in, dev->control.data_length, control_state_machine, dev);
} else {
if (dev->control.data_length == 0) {
dev->control.state = USBH_CONTROL_STATE_STATUS;
device_xfer_control_read(NULL, 0, control_state_machine, dev);
} else {
dev->control.state = USBH_CONTROL_STATE_DATA;
device_xfer_control_write_data(dev->control.data.out, dev->control.data_length, control_state_machine, dev);
}
}
break;
case USBH_CONTROL_STATE_DATA:
if (dev->control.setup_data.bmRequestType & USB_REQ_TYPE_IN) {
dev->control.state = USBH_CONTROL_STATE_NONE;
dev->control.callback(dev, cb_data);
} else {
if (cb_data.status != USBH_PACKET_CALLBACK_STATUS_OK) {
dev->control.state = USBH_CONTROL_STATE_NONE;
// Unable to deliver data control packet - this is a fatal error
usbh_packet_callback_data_t ret_data;
ret_data.status = USBH_PACKET_CALLBACK_STATUS_EFATAL;
ret_data.transferred_length = 0;
dev->control.callback(dev, ret_data);
break;
}
if (dev->control.data_length == 0) {
// we should be in status state when the length of data is zero
LOG_PRINTF("Control logic error\n");
dev->control.state = USBH_CONTROL_STATE_NONE;
dev->control.callback(dev, cb_data);
} else {
dev->control.state = USBH_CONTROL_STATE_STATUS;
device_xfer_control_read(NULL, 0, control_state_machine, dev);
}
}
break;
case USBH_CONTROL_STATE_STATUS:
dev->control.state = USBH_CONTROL_STATE_NONE;
dev->control.callback(dev, cb_data);
break;
default:
break;
}
}
void device_control(usbh_device_t *dev, usbh_packet_callback_t callback, const struct usb_setup_data *setup_data, void *data)
{
if (dev->control.state != USBH_CONTROL_STATE_NONE) {
LOG_PRINTF("ERROR: Use of control state machine while not idle\n");
return;
}
dev->control.state = USBH_CONTROL_STATE_SETUP;
dev->control.callback = callback;
dev->control.data.out = data;
dev->control.data_length = setup_data->wLength;
dev->control.setup_data = *setup_data;
device_xfer_control_write_setup(&dev->control.setup_data, sizeof(dev->control.setup_data), control_state_machine, dev);
}
bool usbh_enum_available(void)
{
return !enumeration();
}
/**
* Returns 0 on error
* device otherwise
*/
usbh_device_t *usbh_get_free_device(const usbh_device_t *dev)
{
const usbh_low_level_driver_t *lld = dev->lld;
usbh_generic_data_t *lld_data = lld->driver_data;
usbh_device_t *usbh_device = lld_data->usbh_device;
uint8_t i;
LOG_PRINTF("DEV ADDRESS%d\n", dev->address);
for (i = 0; i < USBH_MAX_DEVICES; i++) {
if (usbh_device[i].address < 0) {
LOG_PRINTF("\t\t\t\t\tFOUND: %d", i);
usbh_device[i].address = i+1;
return &usbh_device[i];
} else {
LOG_PRINTF("address: %d\n\n\n", usbh_device[i].address);
}
}
return 0;
}
static void device_enumeration_finish(usbh_device_t *dev)
{
reset_enumeration();
dev->state = USBH_ENUM_STATE_FIRST;
}
static void device_enumeration_terminate(usbh_device_t *dev)
{
dev->address = -1;
device_enumeration_finish(dev);
}
#define CONTINUE_WITH(en) \
dev->state = en;\
device_enumerate(dev, cb_data);
static void device_enumerate(usbh_device_t *dev, usbh_packet_callback_data_t cb_data)
{
const usbh_low_level_driver_t *lld = dev->lld;
usbh_generic_data_t *lld_data = lld->driver_data;
uint8_t *usbh_buffer = lld_data->usbh_buffer;
// LOG_PRINTF("\nSTATE: %d\n", state);
switch (dev->state) {
case USBH_ENUM_STATE_SET_ADDRESS:
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
if (dev->address == 0) {
dev->address = usbh_data.address_temporary;
LOG_PRINTF("Assigned address: %d\n", dev->address);
}
CONTINUE_WITH(USBH_ENUM_STATE_DEVICE_DT_READ_SETUP);
break;
default:
device_enumeration_terminate(dev);
ERROR(cb_data.status);
break;
}
break;
case USBH_ENUM_STATE_DEVICE_DT_READ_SETUP:
{
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_IN | USB_REQ_TYPE_DEVICE;
setup_data.bRequest = USB_REQ_GET_DESCRIPTOR;
setup_data.wValue = USB_DT_DEVICE << 8;
setup_data.wIndex = 0;
setup_data.wLength = USB_DT_DEVICE_SIZE;
dev->state = USBH_ENUM_STATE_DEVICE_DT_READ_COMPLETE;
device_control(dev, device_enumerate, &setup_data, &usbh_buffer[0]);
}
break;
case USBH_ENUM_STATE_DEVICE_DT_READ_COMPLETE:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
{
struct usb_device_descriptor *ddt =
(struct usb_device_descriptor *)&usbh_buffer[0];
dev->packet_size_max0 = ddt->bMaxPacketSize0;
LOG_PRINTF("Found device with vid=0x%04x pid=0x%04x\n", ddt->idVendor, ddt->idProduct);
LOG_PRINTF("class=0x%02x subclass=0x%02x protocol=0x%02x\n", ddt->bDeviceClass, ddt->bDeviceSubClass, ddt->bDeviceProtocol);
CONTINUE_WITH(USBH_ENUM_STATE_CONFIGURATION_DT_HEADER_READ_SETUP)
}
break;
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
if (cb_data.transferred_length >= 8) {
struct usb_device_descriptor *ddt =
(struct usb_device_descriptor *)&usbh_buffer[0];
dev->packet_size_max0 = ddt->bMaxPacketSize0;
CONTINUE_WITH(USBH_ENUM_STATE_DEVICE_DT_READ_SETUP);
} else {
device_enumeration_terminate(dev);
ERROR(cb_data.status);
}
break;
default:
device_enumeration_terminate(dev);
ERROR(cb_data.status);
break;
}
}
break;
case USBH_ENUM_STATE_CONFIGURATION_DT_HEADER_READ_SETUP:
{
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_IN | USB_REQ_TYPE_DEVICE;
setup_data.bRequest = USB_REQ_GET_DESCRIPTOR;
setup_data.wValue = USB_DT_CONFIGURATION << 8;
setup_data.wIndex = 0;
setup_data.wLength = dev->packet_size_max0;
dev->state = USBH_ENUM_STATE_CONFIGURATION_DT_HEADER_READ;
device_xfer_control_write_setup(&setup_data, sizeof(setup_data),
device_enumerate, dev);
}
break;
case USBH_ENUM_STATE_CONFIGURATION_DT_HEADER_READ:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
dev->state = USBH_ENUM_STATE_CONFIGURATION_DT_HEADER_READ_COMPLETE;
device_xfer_control_read(&usbh_buffer[USB_DT_DEVICE_SIZE],
dev->packet_size_max0, device_enumerate, dev);
break;
default:
device_enumeration_terminate(dev);
ERROR(cb_data.status);
break;
}
}
break;
case USBH_ENUM_STATE_CONFIGURATION_DT_HEADER_READ_COMPLETE:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
CONTINUE_WITH(USBH_ENUM_STATE_CONFIGURATION_DT_READ_SETUP);
break;
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
if (cb_data.transferred_length >= USB_DT_CONFIGURATION_SIZE) {
struct usb_config_descriptor *cdt =
(struct usb_config_descriptor *)&usbh_buffer[USB_DT_DEVICE_SIZE];
if (cb_data.transferred_length == cdt->wTotalLength) {
LOG_PRINTF("Configuration descriptor read complete. length: %d\n", cdt->wTotalLength);
CONTINUE_WITH(USBH_ENUM_STATE_SET_CONFIGURATION_SETUP);
}
}
break;
default:
device_enumeration_terminate(dev);
ERROR(cb_data.status);
break;
}
}
break;
case USBH_ENUM_STATE_CONFIGURATION_DT_READ_SETUP:
{
struct usb_config_descriptor *cdt =
(struct usb_config_descriptor *)&usbh_buffer[USB_DT_DEVICE_SIZE];
struct usb_setup_data setup_data;
LOG_PRINTF("Getting complete configuration descriptor of length: %d bytes\n", cdt->wTotalLength);
setup_data.bmRequestType = USB_REQ_TYPE_IN | USB_REQ_TYPE_DEVICE;
setup_data.bRequest = USB_REQ_GET_DESCRIPTOR;
setup_data.wValue = USB_DT_CONFIGURATION << 8;
setup_data.wIndex = 0;
setup_data.wLength = cdt->wTotalLength;
dev->state = USBH_ENUM_STATE_CONFIGURATION_DT_READ;
device_xfer_control_write_setup(&setup_data, sizeof(setup_data),
device_enumerate, dev);
}
break;
case USBH_ENUM_STATE_CONFIGURATION_DT_READ:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
{
struct usb_config_descriptor *cdt =
(struct usb_config_descriptor *)&usbh_buffer[USB_DT_DEVICE_SIZE];
dev->state = USBH_ENUM_STATE_CONFIGURATION_DT_READ_COMPLETE;
device_xfer_control_read(&usbh_buffer[USB_DT_DEVICE_SIZE],
cdt->wTotalLength, device_enumerate, dev);
}
break;
default:
device_enumeration_terminate(dev);
ERROR(cb_data.status);
break;
}
}
break;
case USBH_ENUM_STATE_CONFIGURATION_DT_READ_COMPLETE:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
{
struct usb_config_descriptor *cdt =
(struct usb_config_descriptor *)&usbh_buffer[USB_DT_DEVICE_SIZE];
LOG_PRINTF("Configuration descriptor read complete. length: %d\n", cdt->wTotalLength);
CONTINUE_WITH(USBH_ENUM_STATE_SET_CONFIGURATION_SETUP);
}
break;
default:
device_enumeration_terminate(dev);
ERROR(cb_data.status);
break;
}
}
break;
case USBH_ENUM_STATE_SET_CONFIGURATION_SETUP:
{
struct usb_config_descriptor *cdt =
(struct usb_config_descriptor *)&usbh_buffer[USB_DT_DEVICE_SIZE];
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_STANDARD | USB_REQ_TYPE_DEVICE;
setup_data.bRequest = USB_REQ_SET_CONFIGURATION;
setup_data.wValue = cdt->bConfigurationValue;
setup_data.wIndex = 0;
setup_data.wLength = 0;
dev->state = USBH_ENUM_STATE_SET_CONFIGURATION_COMPLETE;
device_control(dev, device_enumerate, &setup_data, 0);
}
break;
case USBH_ENUM_STATE_SET_CONFIGURATION_COMPLETE:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
CONTINUE_WITH(USBH_ENUM_STATE_FIND_DRIVER);
break;
default:
device_enumeration_terminate(dev);
ERROR(cb_data.status);
break;
}
}
break;
case USBH_ENUM_STATE_FIND_DRIVER:
{
struct usb_config_descriptor *cdt =
(struct usb_config_descriptor *)&usbh_buffer[USB_DT_DEVICE_SIZE];
device_register(usbh_buffer, cdt->wTotalLength + USB_DT_DEVICE_SIZE, dev);
device_enumeration_finish(dev);
}
break;
default:
LOG_PRINTF("Error: Unknown state "__FILE__"/%d\n", __LINE__);
break;
}
}
void device_enumeration_start(usbh_device_t *dev)
{
set_enumeration();
// save address
uint8_t address = dev->address;
dev->address = 0;
if (dev->speed == USBH_SPEED_LOW) {
dev->packet_size_max0 = 8;
} else {
dev->packet_size_max0 = 64;
}
usbh_data.address_temporary = address;
LOG_PRINTF("\n\n\n ENUMERATION OF DEVICE@%d STARTED \n\n", address);
dev->state = USBH_ENUM_STATE_SET_ADDRESS;
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_STANDARD | USB_REQ_TYPE_DEVICE;
setup_data.bRequest = USB_REQ_SET_ADDRESS;
setup_data.wValue = address;
setup_data.wIndex = 0;
setup_data.wLength = 0;
device_control(dev, device_enumerate, &setup_data, 0);
}
/**
* Should be called with at least 1kHz frequency
*
*/
void usbh_poll(uint32_t time_curr_us)
{
uint32_t k = 0;
while (usbh_data.lld_drivers[k]) {
usbh_device_t *usbh_device =
((usbh_generic_data_t *)(usbh_data.lld_drivers[k]->driver_data))->usbh_device;
usbh_generic_data_t *lld_data = usbh_data.lld_drivers[k]->driver_data;
enum USBH_POLL_STATUS poll_status =
usbh_data.lld_drivers[k]->poll(lld_data, time_curr_us);
switch (poll_status) {
case USBH_POLL_STATUS_DEVICE_CONNECTED:
// New device found
LOG_PRINTF("\nDEVICE FOUND\n");
usbh_device[0].lld = usbh_data.lld_drivers[k];
usbh_device[0].speed = usbh_data.lld_drivers[k]->root_speed(lld_data);
usbh_device[0].address = 1;
usbh_device[0].control.state = USBH_CONTROL_STATE_NONE;
device_enumeration_start(&usbh_device[0]);
break;
case USBH_POLL_STATUS_DEVICE_DISCONNECTED:
{
usbh_device[0].control.state = USBH_CONTROL_STATE_NONE;
uint32_t i;
for (i = 0; i < USBH_MAX_DEVICES; i++) {
device_remove(&usbh_device[i]);
}
}
break;
default:
break;
}
if (lld_data->usbh_device[0].drv && usbh_device[0].drvdata) {
usbh_device[0].drv->poll(usbh_device[0].drvdata, time_curr_us);
}
k++;
}
}
void usbh_read(usbh_device_t *dev, usbh_packet_t *packet)
{
const usbh_low_level_driver_t *lld = dev->lld;
lld->read(lld->driver_data, packet);
}
void usbh_write(usbh_device_t *dev, const usbh_packet_t *packet)
{
const usbh_low_level_driver_t *lld = dev->lld;
lld->write(lld->driver_data, packet);
}

View file

@ -0,0 +1,364 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2016 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "driver/usbh_device_driver.h"
#include "usbh_driver_ac_midi_private.h"
#include "usart_helpers.h"
#include <stddef.h>
#include <libopencm3/usb/midi.h>
#include <libopencm3/usb/audio.h>
#include <libopencm3/usb/usbstd.h>
static midi_device_t midi_device[USBH_AC_MIDI_MAX_DEVICES];
static const midi_config_t *midi_config = NULL;
static bool initialized = false;
void midi_driver_init(const midi_config_t *config)
{
uint32_t i;
midi_config = config;
for (i = 0; i < USBH_AC_MIDI_MAX_DEVICES; i++) {
midi_device[i].state = 0;
}
initialized = true;
}
/**
*
*
*/
static void *init(usbh_device_t *usbh_dev)
{
if (!midi_config || !initialized) {
LOG_PRINTF("\n%s/%d : driver not initialized\n", __FILE__, __LINE__);
return 0;
}
uint32_t i;
midi_device_t *drvdata = NULL;
// find free data space for midi device
for (i = 0; i < USBH_AC_MIDI_MAX_DEVICES; i++) {
if (midi_device[i].state == 0) {
drvdata = &midi_device[i];
drvdata->device_id = i;
drvdata->endpoint_in_address = 0;
drvdata->endpoint_out_address = 0;
drvdata->endpoint_in_toggle = 0;
drvdata->endpoint_out_toggle = 0;
drvdata->usbh_device = usbh_dev;
drvdata->write_callback_user = NULL;
drvdata->sending = false;
break;
}
}
return drvdata;
}
/**
* Returns true if all needed data are parsed
*/
static bool analyze_descriptor(void *drvdata, void *descriptor)
{
midi_device_t *midi = drvdata;
uint8_t desc_type = ((uint8_t *)descriptor)[1];
switch (desc_type) {
case USB_DT_CONFIGURATION:
{
struct usb_config_descriptor *cfg =
(struct usb_config_descriptor*)descriptor;
midi->buffer[0] = cfg->bConfigurationValue;
}
break;
case USB_DT_DEVICE:
break;
case USB_DT_INTERFACE:
break;
case USB_DT_ENDPOINT:
{
struct usb_endpoint_descriptor *ep =
(struct usb_endpoint_descriptor*)descriptor;
if ((ep->bmAttributes&0x03) == USB_ENDPOINT_ATTR_BULK) {
uint8_t epaddr = ep->bEndpointAddress;
if (epaddr & (1<<7)) {
midi->endpoint_in_address = epaddr&0x7f;
if (ep->wMaxPacketSize < USBH_AC_MIDI_BUFFER) {
midi->endpoint_in_maxpacketsize = ep->wMaxPacketSize;
} else {
midi->endpoint_in_maxpacketsize = USBH_AC_MIDI_BUFFER;
}
} else {
midi->endpoint_out_address = epaddr;
midi->endpoint_out_maxpacketsize = ep->wMaxPacketSize;
}
if (midi->endpoint_in_address && midi->endpoint_out_address) {
midi->state = 1;
return true;
}
}
}
break;
case USB_AUDIO_DT_CS_ENDPOINT:
{
struct usb_midi_in_jack_descriptor *midi_in_jack_desc =
(struct usb_midi_in_jack_descriptor *) descriptor;
(void)midi_in_jack_desc;
}
break;
// TODO Class Specific descriptors
default:
break;
}
return false;
}
static void midi_in_message(midi_device_t *midi, const uint8_t datalen)
{
uint8_t i = 0;
if (midi_config->read_callback) {
for (i = 0; i < datalen; i += 4) {
// uint8_t cable_number = (midi->buffer[i] & 0xf0) >> 4;
uint8_t code_id = midi->buffer[i]&0xf;
uint8_t *ptrdata = &midi->buffer[i];
if (code_id < 2) {
continue;
}
midi_config->read_callback(midi->device_id, ptrdata);
}
}
}
static void event(usbh_device_t *dev, usbh_packet_callback_data_t status)
{
midi_device_t *midi = (midi_device_t *)dev->drvdata;
switch (midi->state) {
case 26:
{
switch (status.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
midi_in_message(midi, midi->endpoint_in_maxpacketsize);
midi->state = 25;
break;
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
midi_in_message(midi, status.transferred_length);
midi->state = 25;
break;
default:
LOG_PRINTF("FATAL ERROR, MIDI DRIVER DEAD \n");
//~ dev->drv->remove();
midi->state = 0;
break;
}
}
break;
case 102:
{
midi->state = 101;
LOG_PRINTF("\n CAN'T TOUCH THIS... ignoring data\n");
}
break;
default:
break;
}
}
static void read_midi_in(void *drvdata, const uint8_t nextstate)
{
midi_device_t *midi = drvdata;
usbh_packet_t packet;
packet.address = midi->usbh_device->address;
packet.data.in = &midi->buffer[0];
packet.datalen = midi->endpoint_in_maxpacketsize;
packet.endpoint_address = midi->endpoint_in_address;
packet.endpoint_size_max = midi->endpoint_in_maxpacketsize;
packet.endpoint_type = USBH_ENDPOINT_TYPE_BULK;
packet.speed = midi->usbh_device->speed;
packet.callback = event;
packet.callback_arg = midi->usbh_device;
packet.toggle = &midi->endpoint_in_toggle;
midi->state = nextstate;
usbh_read(midi->usbh_device,&packet);
}
/**
*
* @param t_us global time us
*/
static void poll(void *drvdata, uint32_t t_us)
{
(void)drvdata;
midi_device_t *midi = drvdata;
switch (midi->state) {
/// Upon configuration, some controllers send additional error data
/// case 100, 101, 102 cares for ignoring those data
case 100:
{
midi->time_us_config = t_us;
midi->state = 101;
}
break;
case 101:
{
read_midi_in(drvdata, 102);
}
break;
case 102:
{
// if elapsed MIDI initial delay microseconds
if (t_us - midi->time_us_config > MIDI_INITIAL_DELAY) {
midi->state = 26;
}
}
break;
case 25:
{
read_midi_in(drvdata, 26);
}
break;
case 1:
{
midi->state = 100;
midi->endpoint_in_toggle = 0;
LOG_PRINTF("\nMIDI CONFIGURED\n");
// Notify user
if (midi_config->notify_connected) {
midi_config->notify_connected(midi->device_id);
}
}
break;
}
}
// don't call directly
static void write_callback(usbh_device_t *dev, usbh_packet_callback_data_t status)
{
(void)status;
midi_device_t *midi = (midi_device_t *)dev->drvdata;
if (midi->sending) {
midi->sending = false;
const midi_write_callback_t callback = midi->write_callback_user;
if (!callback) {
return;
}
if (status.status & USBH_PACKET_CALLBACK_STATUS_OK) {
callback(midi->write_packet.datalen);
} else {
if (status.status & USBH_PACKET_CALLBACK_STATUS_ERRSIZ) {
const uint32_t length = status.transferred_length;
callback(length);
} else {
callback(0);
}
}
}
}
void usbh_midi_write(uint8_t device_id, const void *data, uint32_t length, midi_write_callback_t callback)
{
// bad device_id handling
if (device_id >= USBH_AC_MIDI_MAX_DEVICES) {
return;
}
midi_device_t *midi = &midi_device[device_id];
// device with provided device_id is not alive
if (midi->state == 0) {
return;
}
usbh_device_t *dev = midi->usbh_device;
if (midi->endpoint_out_address == 0) {
return;
}
midi->sending = true;
midi->write_callback_user = callback;
midi->write_packet.data.out = data;
midi->write_packet.datalen = length;
midi->write_packet.address = dev->address;
midi->write_packet.endpoint_address = midi->endpoint_out_address;
midi->write_packet.endpoint_size_max = midi->endpoint_out_maxpacketsize;
midi->write_packet.endpoint_type = USBH_ENDPOINT_TYPE_BULK;
midi->write_packet.speed = dev->speed;
midi->write_packet.callback = write_callback;
midi->write_packet.callback_arg = midi->usbh_device;
midi->write_packet.toggle = &midi->endpoint_out_toggle;
usbh_write(dev, &midi->write_packet);
}
static void remove(void *drvdata)
{
midi_device_t *midi = drvdata;
if (midi_config->notify_disconnected) {
midi_config->notify_disconnected(midi->device_id);
}
midi->state = 0;
midi->endpoint_in_address = 0;
midi->endpoint_out_address = 0;
}
static const usbh_dev_driver_info_t usbh_midi_driver_info = {
.deviceClass = -1,
.deviceSubClass = -1,
.deviceProtocol = -1,
.idVendor = -1,
.idProduct = -1,
.ifaceClass = 0x01,
.ifaceSubClass = 0x03,
.ifaceProtocol = -1,
};
const usbh_dev_driver_t usbh_midi_driver = {
.init = init,
.analyze_descriptor = analyze_descriptor,
.poll = poll,
.remove = remove,
.info = &usbh_midi_driver_info
};

View file

@ -0,0 +1,52 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2016 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef USBH_DRIVER_AC_MIDI_PRIVATE_
#define USBH_DRIVER_AC_MIDI_PRIVATE_
#include "driver/usbh_device_driver.h"
#include "usbh_driver_ac_midi.h"
#include <stdint.h>
#define MIDI_INITIAL_DELAY (100000)
struct _midi_device {
usbh_device_t *usbh_device;
uint8_t buffer[USBH_AC_MIDI_BUFFER];
uint16_t endpoint_in_maxpacketsize;
uint16_t endpoint_out_maxpacketsize;
uint8_t endpoint_in_address;
uint8_t endpoint_out_address;
uint8_t state;
uint8_t endpoint_in_toggle;
uint8_t endpoint_out_toggle;
uint8_t device_id;
bool sending;
midi_write_callback_t write_callback_user;
usbh_packet_t write_packet;
// Timestamp at sending config command
uint32_t time_us_config;
};
typedef struct _midi_device midi_device_t;
#endif

View file

@ -0,0 +1,368 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2015 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "usart_helpers.h"
#include "usbh_driver_gp_xbox.h"
#include "driver/usbh_device_driver.h"
#include <stdint.h>
#include <libopencm3/usb/usbstd.h>
enum STATES {
STATE_INACTIVE,
STATE_INITIAL,
STATE_READING_REQUEST,
STATE_READING_COMPLETE,
};
#define GP_XBOX_CORRECT_TRANSFERRED_LENGTH 20
struct _gp_xbox_device {
usbh_device_t *usbh_device;
uint8_t buffer[USBH_GP_XBOX_BUFFER];
uint16_t endpoint_in_maxpacketsize;
uint8_t endpoint_in_address;
enum STATES state_next;
uint8_t endpoint_in_toggle;
uint8_t device_id;
uint8_t configuration_value;
};
typedef struct _gp_xbox_device gp_xbox_device_t;
static gp_xbox_device_t gp_xbox_device[USBH_GP_XBOX_MAX_DEVICES];
static const gp_xbox_config_t *gp_xbox_config;
static bool initialized = false;
static void read_gp_xbox_in(gp_xbox_device_t *gp_xbox);
void gp_xbox_driver_init(const gp_xbox_config_t *config)
{
if (!config) {
return;
}
initialized = true;
uint32_t i;
gp_xbox_config = config;
for (i = 0; i < USBH_GP_XBOX_MAX_DEVICES; i++) {
gp_xbox_device[i].state_next = STATE_INACTIVE;
}
}
/**
*
*
*/
static void *init(usbh_device_t *usbh_dev)
{
if (!initialized) {
LOG_PRINTF("\n%s/%d : driver not initialized\n", __FILE__, __LINE__);
return 0;
}
uint32_t i;
gp_xbox_device_t *drvdata = 0;
// find free data space for gp_xbox device
for (i = 0; i < USBH_GP_XBOX_MAX_DEVICES; i++) {
if (gp_xbox_device[i].state_next == STATE_INACTIVE) {
drvdata = &gp_xbox_device[i];
drvdata->device_id = i;
drvdata->endpoint_in_address = 0;
drvdata->endpoint_in_toggle = 0;
drvdata->usbh_device = usbh_dev;
break;
}
}
return drvdata;
}
/**
* Returns true if all needed data are parsed
*/
static bool analyze_descriptor(void *drvdata, void *descriptor)
{
gp_xbox_device_t *gp_xbox = (gp_xbox_device_t *)drvdata;
uint8_t desc_type = ((uint8_t *)descriptor)[1];
switch (desc_type) {
case USB_DT_CONFIGURATION:
{
struct usb_config_descriptor *cfg = (struct usb_config_descriptor*)descriptor;
gp_xbox->configuration_value = cfg->bConfigurationValue;
}
break;
case USB_DT_DEVICE:
break;
case USB_DT_INTERFACE:
break;
case USB_DT_ENDPOINT:
{
struct usb_endpoint_descriptor *ep = (struct usb_endpoint_descriptor*)descriptor;
if ((ep->bmAttributes&0x03) == USB_ENDPOINT_ATTR_INTERRUPT) {
uint8_t epaddr = ep->bEndpointAddress;
if (epaddr & (1<<7)) {
gp_xbox->endpoint_in_address = epaddr&0x7f;
if (ep->wMaxPacketSize < USBH_GP_XBOX_BUFFER) {
gp_xbox->endpoint_in_maxpacketsize = ep->wMaxPacketSize;
} else {
gp_xbox->endpoint_in_maxpacketsize = USBH_GP_XBOX_BUFFER;
}
}
if (gp_xbox->endpoint_in_address) {
gp_xbox->state_next = STATE_INITIAL;
return true;
}
}
}
break;
// TODO Class Specific descriptors
default:
break;
}
return false;
}
static void parse_data(usbh_device_t *dev)
{
gp_xbox_device_t *gp_xbox = (gp_xbox_device_t *)dev->drvdata;
uint8_t *packet = gp_xbox->buffer;
gp_xbox_packet_t gp_xbox_packet;
gp_xbox_packet.buttons = 0;
// DPAD
const uint8_t data1 = packet[2];
const uint8_t data2 = packet[3];
if (data1 & (1 << 0)) {
gp_xbox_packet.buttons |= GP_XBOX_DPAD_TOP;
}
if (data1 & (1 << 1)) {
gp_xbox_packet.buttons |= GP_XBOX_DPAD_BOTTOM;
}
if (data1 & (1 << 2)) {
gp_xbox_packet.buttons |= GP_XBOX_DPAD_LEFT;
}
if (data1 & (1 << 3)) {
gp_xbox_packet.buttons |= GP_XBOX_DPAD_RIGHT;
}
// Start + select
if (data1 & (1 << 4)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_START;
}
if (data1 & (1 << 5)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_SELECT;
}
// axis buttons
if (data1 & (1 << 6)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_AXIS_LEFT;
}
if (data1 & (1 << 7)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_AXIS_RIGHT;
}
// buttons ABXY
if (data2 & (1 << 4)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_A;
}
if (data2 & (1 << 5)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_B;
}
if (data2 & (1 << 6)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_X;
}
if (data2 & (1 << 7)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_Y;
}
// buttons rear
if (data2 & (1 << 0)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_LT;
}
if (data2 & (1 << 1)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_RT;
}
if (data2 & (1 << 2)) {
gp_xbox_packet.buttons |= GP_XBOX_BUTTON_XBOX;
}
// rear levers
gp_xbox_packet.axis_rear_left = packet[4];
gp_xbox_packet.axis_rear_right = packet[5];
gp_xbox_packet.axis_left_x = packet[7]*256 + packet[6];
gp_xbox_packet.axis_left_y = packet[9]*256 + packet[8];
gp_xbox_packet.axis_right_x = packet[11]*256 + packet[10];
gp_xbox_packet.axis_right_y = packet[13]*256 + packet[12];
// call update callback
if (gp_xbox_config->update) {
gp_xbox_config->update(gp_xbox->device_id, gp_xbox_packet);
}
}
static void event(usbh_device_t *dev, usbh_packet_callback_data_t cb_data)
{
gp_xbox_device_t *gp_xbox = (gp_xbox_device_t *)dev->drvdata;
switch (gp_xbox->state_next) {
case STATE_READING_COMPLETE:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
parse_data(dev);
gp_xbox->state_next = STATE_READING_REQUEST;
break;
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
if (cb_data.transferred_length == GP_XBOX_CORRECT_TRANSFERRED_LENGTH) {
parse_data(dev);
}
gp_xbox->state_next = STATE_READING_REQUEST;
break;
default:
ERROR(cb_data.status);
gp_xbox->state_next = STATE_INACTIVE;
break;
}
}
break;
case STATE_INACTIVE:
{
LOG_PRINTF("XBOX inactive");
}
break;
default:
{
LOG_PRINTF("Unknown state\n");
}
break;
}
}
static void read_gp_xbox_in(gp_xbox_device_t *gp_xbox)
{
usbh_packet_t packet;
packet.address = gp_xbox->usbh_device->address;
packet.data.in = &gp_xbox->buffer[0];
packet.datalen = gp_xbox->endpoint_in_maxpacketsize;
packet.endpoint_address = gp_xbox->endpoint_in_address;
packet.endpoint_size_max = gp_xbox->endpoint_in_maxpacketsize;
packet.endpoint_type = USBH_ENDPOINT_TYPE_INTERRUPT;
packet.speed = gp_xbox->usbh_device->speed;
packet.callback = event;
packet.callback_arg = gp_xbox->usbh_device;
packet.toggle = &gp_xbox->endpoint_in_toggle;
gp_xbox->state_next = STATE_READING_COMPLETE;
usbh_read(gp_xbox->usbh_device, &packet);
// LOG_PRINTF("@gp_xbox EP1 | \n");
}
/**
* \param time_curr_us - monotically rising time (see usbh_hubbed.h)
* unit is microseconds
*/
static void poll(void *drvdata, uint32_t time_curr_us)
{
(void)time_curr_us;
gp_xbox_device_t *gp_xbox = (gp_xbox_device_t *)drvdata;
switch (gp_xbox->state_next) {
case STATE_READING_REQUEST:
{
read_gp_xbox_in(gp_xbox);
}
break;
case STATE_INITIAL:
{
gp_xbox->state_next = STATE_READING_REQUEST;
gp_xbox->endpoint_in_toggle = 0;
LOG_PRINTF("\ngp_xbox CONFIGURED\n");
if (gp_xbox_config->notify_connected) {
gp_xbox_config->notify_connected(gp_xbox->device_id);
}
}
break;
default:
{
// do nothing - probably transfer is in progress
}
break;
}
}
static void remove(void *drvdata)
{
LOG_PRINTF("Removing xbox\n");
gp_xbox_device_t *gp_xbox = (gp_xbox_device_t *)drvdata;
if (gp_xbox_config->notify_disconnected) {
gp_xbox_config->notify_disconnected(gp_xbox->device_id);
}
gp_xbox->state_next = STATE_INACTIVE;
gp_xbox->endpoint_in_address = 0;
}
static const usbh_dev_driver_info_t driver_info = {
.deviceClass = 0xff,
.deviceSubClass = 0xff,
.deviceProtocol = 0xff,
.idVendor = 0x045e,
.idProduct = 0x028e,
.ifaceClass = 0xff,
.ifaceSubClass = 93,
.ifaceProtocol = 0x01
};
const usbh_dev_driver_t usbh_gp_xbox_driver = {
.init = init,
.analyze_descriptor = analyze_descriptor,
.poll = poll,
.remove = remove,
.info = &driver_info
};

412
fw/src/usbh_driver_hid.c Normal file
View file

@ -0,0 +1,412 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2016 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "usbh_core.h"
#include "driver/usbh_device_driver.h"
#include "usbh_driver_hid.h"
#include "usart_helpers.h"
#include <libopencm3/usb/usbstd.h>
#include <libopencm3/usb/hid.h>
#include <stdint.h>
#include <stddef.h>
#define USB_HID_SET_REPORT 0x09
#define USB_HID_SET_IDLE 0x0A
enum STATES {
STATE_INACTIVE,
STATE_READING_REQUEST,
STATE_READING_COMPLETE_AND_CHECK_REPORT,
STATE_SET_REPORT_EMPTY_READ,
STATE_GET_REPORT_DESCRIPTOR_READ_SETUP,// configuration is complete at this point. We write request
STATE_GET_REPORT_DESCRIPTOR_READ_COMPLETE,// after the read finishes, we parse that descriptor
STATE_SET_IDLE,
STATE_SET_IDLE_COMPLETE,
};
enum REPORT_STATE {
REPORT_STATE_NULL,
REPORT_STATE_READY,
REPORT_STATE_PENDING,
};
struct _hid_device {
usbh_device_t *usbh_device;
uint8_t buffer[USBH_HID_BUFFER];
uint16_t endpoint_in_maxpacketsize;
uint8_t endpoint_in_address;
enum STATES state_next;
uint8_t endpoint_in_toggle;
uint8_t device_id;
uint8_t configuration_value;
uint16_t report0_length;
enum REPORT_STATE report_state;
uint8_t report_data[USBH_HID_REPORT_BUFFER];
uint8_t report_data_length;
enum HID_TYPE hid_type;
uint8_t interface_number;
};
typedef struct _hid_device hid_device_t;
struct hid_report_decriptor {
struct usb_hid_descriptor header;
struct _report_descriptor_info {
uint8_t bDescriptorType;
uint16_t wDescriptorLength;
} __attribute__((packed)) report_descriptors_info[];
} __attribute__((packed));
static hid_device_t hid_device[USBH_HID_MAX_DEVICES];
static hid_config_t hid_config;
static bool initialized = false;
void hid_driver_init(const hid_config_t *config)
{
uint32_t i;
initialized = true;
hid_config = *config;
for (i = 0; i < USBH_HID_MAX_DEVICES; i++) {
hid_device[i].state_next = STATE_INACTIVE;
}
}
static void *init(usbh_device_t *usbh_dev)
{
if (!initialized) {
LOG_PRINTF("\n%s/%d : driver not initialized\n", __FILE__, __LINE__);
return 0;
}
uint32_t i;
hid_device_t *drvdata = NULL;
// find free data space for HID device
for (i = 0; i < USBH_HID_MAX_DEVICES; i++) {
if (hid_device[i].state_next == STATE_INACTIVE) {
drvdata = &hid_device[i];
drvdata->device_id = i;
drvdata->endpoint_in_address = 0;
drvdata->endpoint_in_toggle = 0;
drvdata->report0_length = 0;
drvdata->usbh_device = usbh_dev;
drvdata->report_state = REPORT_STATE_NULL;
drvdata->hid_type = HID_TYPE_NONE;
break;
}
}
return drvdata;
}
static void parse_report_descriptor(hid_device_t *hid, const uint8_t *buffer, uint32_t length)
{
// TODO
// Do some parsing!
// add some checks
hid->report_state = REPORT_STATE_READY;
// TODO: parse this from buffer!
hid->report_data_length = 1;
(void)buffer;
(void)length;
}
/**
* Returns true if all needed data are parsed
*/
static bool analyze_descriptor(void *drvdata, void *descriptor)
{
hid_device_t *hid = (hid_device_t *)drvdata;
uint8_t desc_type = ((uint8_t *)descriptor)[1];
switch (desc_type) {
case USB_DT_CONFIGURATION:
{
const struct usb_config_descriptor * cfg = (const struct usb_config_descriptor*)descriptor;
hid->configuration_value = cfg->bConfigurationValue;
}
break;
case USB_DT_DEVICE:
{
const struct usb_device_descriptor *devDesc = (const struct usb_device_descriptor *)descriptor;
(void)devDesc;
}
break;
case USB_DT_INTERFACE:
{
const struct usb_interface_descriptor *ifDesc = (const struct usb_interface_descriptor *)descriptor;
if (ifDesc->bInterfaceProtocol == 0x01) {
hid->hid_type = HID_TYPE_KEYBOARD;
hid->interface_number = ifDesc->bInterfaceNumber;
} else if (ifDesc->bInterfaceProtocol == 0x02) {
hid->hid_type = HID_TYPE_MOUSE;
hid->interface_number = ifDesc->bInterfaceNumber;
}
}
break;
case USB_DT_ENDPOINT:
{
const struct usb_endpoint_descriptor *ep = (const struct usb_endpoint_descriptor *)descriptor;
if ((ep->bmAttributes&0x03) == USB_ENDPOINT_ATTR_INTERRUPT) {
uint8_t epaddr = ep->bEndpointAddress;
if (epaddr & (1<<7)) {
hid->endpoint_in_address = epaddr&0x7f;
if (ep->wMaxPacketSize < USBH_HID_BUFFER) {
hid->endpoint_in_maxpacketsize = ep->wMaxPacketSize;
} else {
hid->endpoint_in_maxpacketsize = USBH_HID_BUFFER;
}
}
}
}
break;
case USB_DT_HID:
{
const struct hid_report_decriptor *desc = (const struct hid_report_decriptor *)descriptor;
if (desc->header.bNumDescriptors > 0 && desc->report_descriptors_info[0].bDescriptorType == USB_DT_REPORT) {
hid->report0_length = desc->report_descriptors_info[0].wDescriptorLength;
}
}
break;
default:
break;
}
if (hid->endpoint_in_address && hid->report0_length) {
hid->state_next = STATE_GET_REPORT_DESCRIPTOR_READ_SETUP;
return true;
}
return false;
}
static void report_event(usbh_device_t *dev, usbh_packet_callback_data_t cb_data)
{
(void)cb_data;// UNUSED
hid_device_t *hid = (hid_device_t *)dev->drvdata;
hid->report_state = REPORT_STATE_READY;
}
static void event(usbh_device_t *dev, usbh_packet_callback_data_t cb_data)
{
hid_device_t *hid = (hid_device_t *)dev->drvdata;
switch (hid->state_next) {
case STATE_READING_COMPLETE_AND_CHECK_REPORT:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
if (hid_config.hid_in_message_handler) {
hid_config.hid_in_message_handler(hid->device_id, hid->buffer, cb_data.transferred_length);
}
hid->state_next = STATE_READING_REQUEST;
break;
default:
ERROR(cb_data.status);
hid->state_next = STATE_INACTIVE;
break;
}
}
break;
case STATE_GET_REPORT_DESCRIPTOR_READ_COMPLETE: // read complete, SET_IDLE to 0
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
LOG_PRINTF("READ REPORT COMPLETE \n");
hid->state_next = STATE_READING_REQUEST;
hid->endpoint_in_toggle = 0;
parse_report_descriptor(hid, hid->buffer, cb_data.transferred_length);
break;
default:
ERROR(cb_data.status);
hid->state_next = STATE_INACTIVE;
break;
}
}
break;
default:
break;
}
}
static void read_hid_in_endpoint(void *drvdata)
{
hid_device_t *hid = (hid_device_t *)drvdata;
usbh_packet_t packet;
packet.address = hid->usbh_device->address;
packet.data.in = &hid->buffer[0];
packet.datalen = hid->endpoint_in_maxpacketsize;
packet.endpoint_address = hid->endpoint_in_address;
packet.endpoint_size_max = hid->endpoint_in_maxpacketsize;
packet.endpoint_type = USBH_ENDPOINT_TYPE_INTERRUPT;
packet.speed = hid->usbh_device->speed;
packet.callback = event;
packet.callback_arg = hid->usbh_device;
packet.toggle = &hid->endpoint_in_toggle;
hid->state_next = STATE_READING_COMPLETE_AND_CHECK_REPORT;
usbh_read(hid->usbh_device, &packet);
}
/**
* @param time_curr_us - monotically rising time
* unit is microseconds
* @see usbh_poll()
*/
static void poll(void *drvdata, uint32_t time_curr_us)
{
(void)time_curr_us;
hid_device_t *hid = (hid_device_t *)drvdata;
usbh_device_t *dev = hid->usbh_device;
switch (hid->state_next) {
case STATE_READING_REQUEST:
{
read_hid_in_endpoint(drvdata);
}
break;
case STATE_GET_REPORT_DESCRIPTOR_READ_SETUP:
{
hid->endpoint_in_toggle = 0;
// We support only the first report descriptor with index 0
// limit the size of the report descriptor!
if (hid->report0_length > USBH_HID_BUFFER) {
hid->report0_length = USBH_HID_BUFFER;
}
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_IN | USB_REQ_TYPE_INTERFACE;
setup_data.bRequest = USB_REQ_GET_DESCRIPTOR;
setup_data.wValue = USB_DT_REPORT << 8;
setup_data.wIndex = 0;
setup_data.wLength = hid->report0_length;
hid->state_next = STATE_GET_REPORT_DESCRIPTOR_READ_COMPLETE;
device_control(dev, event, &setup_data, hid->buffer);
}
break;
default:
// do nothing - probably transfer is in progress
break;
}
}
static void remove(void *drvdata)
{
hid_device_t *hid = (hid_device_t *)drvdata;
hid->state_next = STATE_INACTIVE;
hid->endpoint_in_address = 0;
}
bool hid_set_report(uint8_t device_id, uint8_t val)
{
if (device_id >= USBH_HID_MAX_DEVICES) {
LOG_PRINTF("invalid device id\n");
return false;
}
hid_device_t *hid = &hid_device[device_id];
if (hid->report_state != REPORT_STATE_READY) {
LOG_PRINTF("reporting is not ready\n");
// store and update afterwards
return false;
}
if (hid->report_data_length == 0) {
LOG_PRINTF("reporting is not available (report len=0)\n");
return false;
}
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE;
setup_data.bRequest = USB_HID_SET_REPORT;
setup_data.wValue = 0x02 << 8;
setup_data.wIndex = hid->interface_number;
setup_data.wLength = hid->report_data_length;
hid->report_data[0] = val;
hid->report_state = REPORT_STATE_PENDING;
device_control(hid->usbh_device, report_event, &setup_data, &hid->report_data);
return true;
}
bool hid_is_connected(uint8_t device_id)
{
if (device_id >= USBH_HID_MAX_DEVICES) {
LOG_PRINTF("is connected: invalid device id\n");
return false;
}
return hid_device[device_id].state_next == STATE_INACTIVE;
}
enum HID_TYPE hid_get_type(uint8_t device_id)
{
if (hid_is_connected(device_id)) {
return HID_TYPE_NONE;
}
return hid_device[device_id].hid_type;
}
static const usbh_dev_driver_info_t driver_info = {
.deviceClass = -1,
.deviceSubClass = -1,
.deviceProtocol = -1,
.idVendor = -1,
.idProduct = -1,
.ifaceClass = 0x03, // HID class
.ifaceSubClass = -1,
.ifaceProtocol = -1, // Do not care
};
const usbh_dev_driver_t usbh_hid_driver = {
.init = init,
.analyze_descriptor = analyze_descriptor,
.poll = poll,
.remove = remove,
.info = &driver_info
};

675
fw/src/usbh_driver_hub.c Normal file
View file

@ -0,0 +1,675 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2015 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "usbh_driver_hub_private.h"
#include "driver/usbh_device_driver.h"
#include "usart_helpers.h"
#include "usbh_config.h"
#include <stddef.h>
#include <stdint.h>
static hub_device_t hub_device[USBH_MAX_HUBS];
static bool initialized = false;
void hub_driver_init(void)
{
uint32_t i;
initialized = true;
for (i = 0; i < USBH_MAX_HUBS; i++) {
hub_device[i].device[0] = 0;
hub_device[i].ports_num = 0;
hub_device[i].current_port = CURRENT_PORT_NONE;
}
}
static void *init(usbh_device_t *usbh_dev)
{
if (!initialized) {
LOG_PRINTF("\n%s/%d : driver not initialized\n", __FILE__, __LINE__);
return 0;
}
uint32_t i;
hub_device_t *drvdata = NULL;
// find free data space for hub device
for (i = 0; i < USBH_MAX_HUBS; i++) {
if (hub_device[i].device[0] == 0) {
break;
}
}
LOG_PRINTF("{%d}",i);
if (i == USBH_MAX_HUBS) {
LOG_PRINTF("Unable to initialize hub driver");
return 0;
}
drvdata = &hub_device[i];
drvdata->state = EVENT_STATE_NONE;
drvdata->ports_num = 0;
drvdata->device[0] = usbh_dev;
drvdata->busy = 0;
drvdata->endpoint_in_address = 0;
drvdata->endpoint_in_maxpacketsize = 0;
return drvdata;
}
/**
* @returns true if all needed data are parsed
*/
static bool analyze_descriptor(void *drvdata, void *descriptor)
{
hub_device_t *hub = (hub_device_t *)drvdata;
uint8_t desc_type = ((uint8_t *)descriptor)[1];
switch (desc_type) {
case USB_DT_ENDPOINT:
{
struct usb_endpoint_descriptor *ep = (struct usb_endpoint_descriptor *)descriptor;
if ((ep->bmAttributes&0x03) == USB_ENDPOINT_ATTR_INTERRUPT) {
uint8_t epaddr = ep->bEndpointAddress;
if (epaddr & (1<<7)) {
hub->endpoint_in_address = epaddr&0x7f;
hub->endpoint_in_maxpacketsize = ep->wMaxPacketSize;
}
}
LOG_PRINTF("ENDPOINT DESCRIPTOR FOUND\n");
}
break;
case USB_DT_HUB:
{
struct usb_hub_descriptor *desc = (struct usb_hub_descriptor *)descriptor;
if ( desc->head.bNbrPorts <= USBH_HUB_MAX_DEVICES) {
hub->ports_num = desc->head.bNbrPorts;
} else {
LOG_PRINTF("INCREASE NUMBER OF ENABLED PORTS\n");
hub->ports_num = USBH_HUB_MAX_DEVICES;
}
LOG_PRINTF("HUB DESCRIPTOR FOUND \n");
}
break;
default:
LOG_PRINTF("TYPE: %02X \n",desc_type);
break;
}
if (hub->endpoint_in_address) {
hub->state = EVENT_STATE_INITIAL;
LOG_PRINTF("end enum");
return true;
}
return false;
}
// Enumerate
static void event(usbh_device_t *dev, usbh_packet_callback_data_t cb_data)
{
hub_device_t *hub = (hub_device_t *)dev->drvdata;
LOG_PRINTF("\nHUB->STATE = %d\n", hub->state);
switch (hub->state) {
case EVENT_STATE_POLL:
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
{
uint8_t i;
uint8_t *buf = hub->buffer;
uint32_t psc = 0; // Limit: up to 4 bytes...
for (i = 0; i < cb_data.transferred_length; i++) {
psc += buf[i] << (i*8);
}
int8_t port = 0;
LOG_PRINTF("psc:%d\n",psc);
// Driver error... port not found
if (!psc) {
// Continue reading status change endpoint
hub->state = EVENT_STATE_POLL_REQ;
break;
}
for (i = 0; i <= hub->ports_num; i++) {
if (psc & (1<<i)) {
port = i;
psc = 0;
break;
}
}
if (hub->current_port >= 1) {
if (hub->current_port != port) {
LOG_PRINTF("X");
hub->state = EVENT_STATE_POLL_REQ;
break;
}
}
struct usb_setup_data setup_data;
// If regular port event, else hub event
if (port) {
setup_data.bmRequestType = USB_REQ_TYPE_IN | USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE | USB_REQ_TYPE_ENDPOINT;
} else {
setup_data.bmRequestType = USB_REQ_TYPE_IN | USB_REQ_TYPE_CLASS | USB_REQ_TYPE_DEVICE;
}
setup_data.bRequest = USB_REQ_GET_STATUS;
setup_data.wValue = 0;
setup_data.wIndex = port;
setup_data.wLength = 4;
hub->state = EVENT_STATE_GET_STATUS_COMPLETE;
hub->current_port = port;
LOG_PRINTF("\n\nPORT FOUND: %d\n", port);
device_control(dev, event, &setup_data, &hub->hub_and_port_status[port]);
}
break;
default:
ERROR(cb_data.status);
hub->state = EVENT_STATE_NONE;
break;
case USBH_PACKET_CALLBACK_STATUS_EAGAIN:
// In case of EAGAIN error, retry read on status endpoint
hub->state = EVENT_STATE_POLL_REQ;
LOG_PRINTF("HUB: Retrying...\n");
break;
}
break;
case EVENT_STATE_READ_HUB_DESCRIPTOR_COMPLETE:// Hub descriptor found
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
{
struct usb_hub_descriptor *hub_descriptor =
(struct usb_hub_descriptor *)hub->buffer;
// Check size
if (hub_descriptor->head.bDescLength > hub->desc_len) {
struct usb_setup_data setup_data;
hub->desc_len = hub_descriptor->head.bDescLength;
setup_data.bmRequestType = USB_REQ_TYPE_IN | USB_REQ_TYPE_CLASS | USB_REQ_TYPE_DEVICE;
setup_data.bRequest = USB_REQ_GET_DESCRIPTOR;
setup_data.wValue = 0x29<<8;
setup_data.wIndex = 0;
setup_data.wLength = hub->desc_len;
hub->state = EVENT_STATE_READ_HUB_DESCRIPTOR_COMPLETE;
device_control(dev, event, &setup_data, hub->buffer);
break;
} else if (hub_descriptor->head.bDescLength == hub->desc_len) {
hub->ports_num = hub_descriptor->head.bNbrPorts;
hub->state = EVENT_STATE_ENABLE_PORTS;
hub->index = 0;
cb_data.status = USBH_PACKET_CALLBACK_STATUS_OK;
event(dev, cb_data);
} else {
//try again
}
}
break;
case USBH_PACKET_CALLBACK_STATUS_ERRSIZ:
{
LOG_PRINTF("->\t\t\t\t\t ERRSIZ: deschub\n");
struct usb_hub_descriptor*hub_descriptor =
(struct usb_hub_descriptor *)hub->buffer;
if (cb_data.transferred_length >= sizeof(struct usb_hub_descriptor_head)) {
if (cb_data.transferred_length == hub_descriptor->head.bDescLength) {
// Process HUB descriptor
if ( hub_descriptor->head.bNbrPorts <= USBH_HUB_MAX_DEVICES) {
hub->ports_num = hub_descriptor->head.bNbrPorts;
} else {
LOG_PRINTF("INCREASE NUMBER OF ENABLED PORTS\n");
hub->ports_num = USBH_HUB_MAX_DEVICES;
}
hub->state = EVENT_STATE_ENABLE_PORTS;
hub->index = 0;
cb_data.status = USBH_PACKET_CALLBACK_STATUS_OK;
event(dev, cb_data);
}
}
}
break;
default:
ERROR(cb_data.status);
break;
}
}
break;
case EVENT_STATE_ENABLE_PORTS:// enable ports
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
if (hub->index < hub->ports_num) {
hub->index++;
struct usb_setup_data setup_data;
LOG_PRINTF("[!%d!]",hub->index);
setup_data.bmRequestType = USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE | USB_REQ_TYPE_ENDPOINT;
setup_data.bRequest = HUB_REQ_SET_FEATURE;
setup_data.wValue = HUB_FEATURE_PORT_POWER;
setup_data.wIndex = hub->index;
setup_data.wLength = 0;
device_control(dev, event, &setup_data, 0);
} else {
// TODO:
// Delay Based on hub descriptor field bPwr2PwrGood
// delay_ms_busy_loop(200);
LOG_PRINTF("\nHUB CONFIGURED & PORTS POWERED\n");
// get device status
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_IN | USB_REQ_TYPE_CLASS | USB_REQ_TYPE_DEVICE;
setup_data.bRequest = USB_REQ_GET_STATUS;
setup_data.wValue = 0;
setup_data.wIndex = 0;
setup_data.wLength = 4;
hub->state = EVENT_STATE_GET_PORT_STATUS;
hub->index = 0;
device_control(dev, event, &setup_data, hub->buffer);
}
break;
default:
ERROR(cb_data.status);
break;
}
}
break;
case EVENT_STATE_GET_PORT_STATUS:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
{
if (hub->index < hub->ports_num) {
//TODO: process data contained in hub->buffer
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_IN | USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE | USB_REQ_TYPE_ENDPOINT;
setup_data.bRequest = USB_REQ_GET_STATUS;
setup_data.wValue = 0;
setup_data.wIndex = ++hub->index;
setup_data.wLength = 4;
hub->state = EVENT_STATE_GET_PORT_STATUS;
device_control(dev, event, &setup_data, hub->buffer);
} else {
hub->busy = 0;
hub->state = EVENT_STATE_POLL_REQ;
}
}
break;
default:
ERROR(cb_data.status);
break;
}
}
break;
case EVENT_STATE_GET_STATUS_COMPLETE:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
{
int8_t port = hub->current_port;
LOG_PRINTF("|%d",port);
// Get Port status, else Get Hub status
if (port) {
uint16_t stc = hub->hub_and_port_status[port].stc;
// Connection status changed
if (stc & (1<<HUB_FEATURE_PORT_CONNECTION)) {
// Check, whether device is in connected state
if (!hub->device[port]) {
if (!usbh_enum_available() || hub->busy) {
LOG_PRINTF("\n\t\t\tCannot enumerate %d %d\n", !usbh_enum_available(), hub->busy);
hub->state = EVENT_STATE_POLL_REQ;
break;
}
}
// clear feature C_PORT_CONNECTION
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE | USB_REQ_TYPE_ENDPOINT;
setup_data.bRequest = HUB_REQ_CLEAR_FEATURE;
setup_data.wValue = HUB_FEATURE_C_PORT_CONNECTION;
setup_data.wIndex = port;
setup_data.wLength = 0;
hub->state = EVENT_STATE_PORT_RESET_REQ;
device_control(dev, event, &setup_data, 0);
} else if(stc & (1<<HUB_FEATURE_PORT_RESET)) {
// clear feature C_PORT_RESET
// Reset processing is complete, enumerate device
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE | USB_REQ_TYPE_ENDPOINT;
setup_data.bRequest = HUB_REQ_CLEAR_FEATURE;
setup_data.wValue = HUB_FEATURE_C_PORT_RESET;
setup_data.wIndex = port;
setup_data.wLength = 0;
hub->state = EVENT_STATE_PORT_RESET_COMPLETE;
LOG_PRINTF("RESET");
device_control(dev, event, &setup_data, 0);
} else {
LOG_PRINTF("another STC %d\n", stc);
}
} else {
hub->state = EVENT_STATE_POLL_REQ;
LOG_PRINTF("HUB status change\n");
}
}
break;
default:
ERROR(cb_data.status);
// continue
hub->state = EVENT_STATE_POLL_REQ;
break;
}
}
break;
case EVENT_STATE_PORT_RESET_REQ:
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
{
int8_t port = hub->current_port;
uint16_t stc = hub->hub_and_port_status[port].stc;
if (!hub->device[port]) {
if ((stc) & (1<<HUB_FEATURE_PORT_CONNECTION)) {
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE | USB_REQ_TYPE_ENDPOINT;
setup_data.bRequest = HUB_REQ_SET_FEATURE;
setup_data.wValue = HUB_FEATURE_PORT_RESET;
setup_data.wIndex = port;
setup_data.wLength = 0;
hub->state = EVENT_STATE_GET_PORT_STATUS;
LOG_PRINTF("CONN");
hub->busy = 1;
device_control(dev, event, &setup_data, 0);
}
} else {
LOG_PRINTF("\t\t\t\tDISCONNECT EVENT\n");
device_remove(hub->device[port]);
hub->device[port] = 0;
hub->current_port = CURRENT_PORT_NONE;
hub->state = EVENT_STATE_POLL_REQ;
hub->busy = 0;
}
}
break;
default:
ERROR(cb_data.status);
// continue
hub->state = EVENT_STATE_POLL_REQ;
break;
}
}
break;
case EVENT_STATE_PORT_RESET_COMPLETE: // RESET COMPLETE, start enumeration
{
switch (cb_data.status) {
case USBH_PACKET_CALLBACK_STATUS_OK:
{
LOG_PRINTF("\nPOLL\n");
int8_t port = hub->current_port;
uint16_t sts = hub->hub_and_port_status[port].sts;
if (sts & (1<<HUB_FEATURE_PORT_ENABLE)) {
hub->device[port] = usbh_get_free_device(dev);
if (!hub->device[port]) {
LOG_PRINTF("\nFATAL ERROR\n");
return;// DEAD END
}
if ((sts & (1<<(HUB_FEATURE_PORT_LOWSPEED))) &&
!(sts & (1<<(HUB_FEATURE_PORT_HIGHSPEED)))) {
#define DISABLE_LOW_SPEED
#ifdef DISABLE_LOW_SPEED
LOG_PRINTF("Low speed device");
// Disable Low speed device immediately
struct usb_setup_data setup_data;
setup_data.bmRequestType = USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE | USB_REQ_TYPE_ENDPOINT;
setup_data.bRequest = HUB_REQ_CLEAR_FEATURE;
setup_data.wValue = HUB_FEATURE_PORT_ENABLE;
setup_data.wIndex = port;
setup_data.wLength = 0;
// After write process another devices, poll for events
//Expecting all ports are powered (constant/non-changeable after init)
hub->state = EVENT_STATE_GET_PORT_STATUS;
hub->current_port = CURRENT_PORT_NONE;
device_control(dev, event, &setup_data, 0);
#else
hub->device[port]->speed = USBH_SPEED_LOW;
LOG_PRINTF("Low speed device");
hub->timestamp_us = hub->time_curr_us;
hub->state = EVENT_STATE_SLEEP_500_MS; // schedule wait for 500ms
#endif
} else if (!(sts & (1<<(HUB_FEATURE_PORT_LOWSPEED))) &&
!(sts & (1<<(HUB_FEATURE_PORT_HIGHSPEED)))) {
hub->device[port]->speed = USBH_SPEED_FULL;
LOG_PRINTF("Full speed device");
hub->timestamp_us = hub->time_curr_us;
hub->state = EVENT_STATE_SLEEP_500_MS; // schedule wait for 500ms
}
} else {
LOG_PRINTF("%s:%d Do not know what to do, when device is disabled after reset\n", __FILE__, __LINE__);
hub->state = EVENT_STATE_POLL_REQ;
return;
}
}
break;
default:
ERROR(cb_data.status);
// continue
hub->state = EVENT_STATE_POLL_REQ;
break;
}
}
break;
default:
LOG_PRINTF("UNHANDLED EVENT %d\n",hub->state);
break;
}
}
static void read_ep1(void *drvdata)
{
hub_device_t *hub = (hub_device_t *)drvdata;
usbh_packet_t packet;
packet.address = hub->device[0]->address;
packet.data.in = hub->buffer;
packet.datalen = hub->endpoint_in_maxpacketsize;
packet.endpoint_address = hub->endpoint_in_address;
packet.endpoint_size_max = hub->endpoint_in_maxpacketsize;
packet.endpoint_type = USBH_ENDPOINT_TYPE_INTERRUPT;
packet.speed = hub->device[0]->speed;
packet.callback = event;
packet.callback_arg = hub->device[0];
packet.toggle = &hub->endpoint_in_toggle;
hub->state = EVENT_STATE_POLL;
usbh_read(hub->device[0], &packet);
LOG_PRINTF("@hub %d/EP1 | \n", hub->device[0]->address);
}
/**
* @param time_curr_us - monotically rising time
* unit is microseconds
* @see usbh_poll()
*/
static void poll(void *drvdata, uint32_t time_curr_us)
{
hub_device_t *hub = (hub_device_t *)drvdata;
usbh_device_t *dev = hub->device[0];
hub->time_curr_us = time_curr_us;
switch (hub->state) {
case EVENT_STATE_POLL_REQ:
{
if (usbh_enum_available()) {
read_ep1(hub);
} else {
LOG_PRINTF("enum not available\n");
}
}
break;
case EVENT_STATE_INITIAL:
{
if (hub->ports_num) {
hub->index = 0;
hub->state = EVENT_STATE_ENABLE_PORTS;
LOG_PRINTF("No need to get HUB DESC\n");
event(dev, (usbh_packet_callback_data_t){0, 0});
} else {
hub->endpoint_in_toggle = 0;
struct usb_setup_data setup_data;
hub->desc_len = hub->device[0]->packet_size_max0;
setup_data.bmRequestType = USB_REQ_TYPE_IN | USB_REQ_TYPE_CLASS | USB_REQ_TYPE_DEVICE;
setup_data.bRequest = USB_REQ_GET_DESCRIPTOR;
setup_data.wValue = 0x29 << 8;
setup_data.wIndex = 0;
setup_data.wLength = hub->desc_len;
hub->state = EVENT_STATE_READ_HUB_DESCRIPTOR_COMPLETE;
device_control(dev, event, &setup_data, hub->buffer);
LOG_PRINTF("DO Need to get HUB DESC\n");
}
}
break;
case EVENT_STATE_SLEEP_500_MS:
if (hub->time_curr_us - hub->timestamp_us > 500000) {
int8_t port = hub->current_port;
LOG_PRINTF("PORT: %d\n", port);
LOG_PRINTF("NEW device at address: %d\n", hub->device[port]->address);
hub->device[port]->lld = hub->device[0]->lld;
device_enumeration_start(hub->device[port]);
hub->current_port = CURRENT_PORT_NONE;
// Maybe error, when assigning address is taking too long
//
// Detail:
// USB hub cannot enable another port while the device
// the current one is also in address state (has address==0)
// Only one device on bus can have address==0
hub->busy = 0;
hub->state = EVENT_STATE_POLL_REQ;
}
break;
default:
break;
}
if (usbh_enum_available()) {
uint32_t i;
for (i = 1; i < USBH_HUB_MAX_DEVICES + 1; i++) {
if (hub->device[i]) {
if (hub->device[i]->drv && hub->device[i]->drvdata) {
hub->device[i]->drv->poll(hub->device[i]->drvdata, time_curr_us);
}
}
}
}
}
static void remove(void *drvdata)
{
hub_device_t *hub = (hub_device_t *)drvdata;
uint8_t i;
hub->state = EVENT_STATE_NONE;
hub->endpoint_in_address = 0;
hub->busy = 0;
for (i = 0; i < USBH_HUB_MAX_DEVICES + 1; i++) {
hub->device[i] = 0;
}
}
static const usbh_dev_driver_info_t driver_info = {
.deviceClass = 0x09,
.deviceSubClass = -1,
.deviceProtocol = -1,
.idVendor = -1,
.idProduct = -1,
.ifaceClass = 0x09,
.ifaceSubClass = -1,
.ifaceProtocol = -1
};
const usbh_dev_driver_t usbh_hub_driver = {
.init = init,
.analyze_descriptor = analyze_descriptor,
.poll = poll,
.remove = remove,
.info = &driver_info
};

View file

@ -0,0 +1,123 @@
/*
* This file is part of the libusbhost library
* hosted at http://github.com/libusbhost/libusbhost
*
* Copyright (C) 2015 Amir Hammad <amir.hammad@hotmail.com>
*
*
* libusbhost is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef USBH_DRIVER_HUB_PRIVATE_
#define USBH_DRIVER_HUB_PRIVATE_
#include "usbh_config.h"
#include "driver/usbh_device_driver.h"
#include "usbh_driver_hub.h"
#include <stdint.h>
#include <stdbool.h>
#include <libopencm3/usb/usbstd.h>
// # HUB DEFINITIONS
#define HUB_FEATURE_PORT_CONNECTION 0
#define HUB_FEATURE_PORT_ENABLE 1
#define HUB_FEATURE_PORT_SUSPEND 2
#define HUB_FEATURE_PORT_OVERCURRENT 3
#define HUB_FEATURE_PORT_RESET 4
#define HUB_FEATURE_PORT_POWER 8
#define HUB_FEATURE_PORT_LOWSPEED 9
#define HUB_FEATURE_PORT_HIGHSPEED 10
#define HUB_FEATURE_C_PORT_CONNECTION 16
#define HUB_FEATURE_C_PORT_ENABLE 17
#define HUB_FEATURE_C_PORT_SUSPEND 18
#define HUB_FEATURE_C_PORT_OVERCURRENT 19
#define HUB_FEATURE_C_PORT_RESET 20
#define HUB_REQ_GET_STATUS 0
#define HUB_REQ_CLEAR_FEATURE 1
#define HUB_REQ_SET_FEATURE 3
#define HUB_REQ_GET_DESCRIPTOR 6
#define USB_DT_HUB (41)
#define USB_DT_HUB_SIZE (9)
// Hub buffer: must be larger than hub descriptor
#define USBH_HUB_BUFFER_SIZE (USB_DT_HUB_SIZE)
#define CURRENT_PORT_NONE -1
enum EVENT_STATE {
EVENT_STATE_NONE,
EVENT_STATE_INITIAL,
EVENT_STATE_POLL_REQ,
EVENT_STATE_POLL,
EVENT_STATE_READ_HUB_DESCRIPTOR_COMPLETE,
EVENT_STATE_ENABLE_PORTS,
EVENT_STATE_GET_PORT_STATUS,
EVENT_STATE_PORT_RESET_REQ,
EVENT_STATE_PORT_RESET_COMPLETE,
EVENT_STATE_SLEEP_500_MS,
EVENT_STATE_GET_STATUS_COMPLETE,
};
struct _hub_device {
usbh_device_t *device[USBH_HUB_MAX_DEVICES + 1];
uint8_t buffer[USBH_HUB_BUFFER_SIZE];
uint16_t endpoint_in_maxpacketsize;
uint8_t endpoint_in_address;
uint8_t endpoint_in_toggle;
enum EVENT_STATE state;
uint8_t desc_len;
uint16_t ports_num;
int8_t index;
int8_t current_port;
struct {
uint16_t sts;
uint16_t stc;
} hub_and_port_status[USBH_HUB_MAX_DEVICES + 1];
bool busy;
uint32_t time_curr_us;
uint32_t timestamp_us;
};
typedef struct _hub_device hub_device_t;
struct usb_hub_descriptor_head {
uint8_t bDescLength;
uint8_t bDescriptorType;
uint8_t bNbrPorts;
uint16_t wHubCharacteristics;
uint8_t bPwrOn2PwrGood;
uint8_t bHubContrCurrent;
} __attribute__((packed));
struct usb_hub_descriptor_body {
uint8_t bDeviceRemovable;
uint8_t PortPwrCtrlMask;
} __attribute__((packed));
// for hubs with up to 7 ports on hub
struct usb_hub_descriptor {
struct usb_hub_descriptor_head head;
struct usb_hub_descriptor_body body[1];
} __attribute__((packed));
#endif

1041
fw/src/usbh_lld_stm32f4.c Normal file

File diff suppressed because it is too large Load diff

521
fw/src/words.c Normal file
View file

@ -0,0 +1,521 @@
#include "words.h"
const char * const even[256] = {
"aardvark", /* 00 */
"absurd", /* 01 */
"accrue", /* 02 */
"acme", /* 03 */
"adrift", /* 04 */
"adult", /* 05 */
"afflict", /* 06 */
"ahead", /* 07 */
"aimless", /* 08 */
"Algol", /* 09 */
"allow", /* 0A */
"alone", /* 0B */
"ammo", /* 0C */
"ancient", /* 0D */
"apple", /* 0E */
"artist", /* 0F */
"assume", /* 10 */
"Athens", /* 11 */
"atlas", /* 12 */
"Aztec", /* 13 */
"baboon", /* 14 */
"backfield", /* 15 */
"backward", /* 16 */
"banjo", /* 17 */
"beaming", /* 18 */
"bedlamp", /* 19 */
"beehive", /* 1A */
"beeswax", /* 1B */
"befriend", /* 1C */
"Belfast", /* 1D */
"berserk", /* 1E */
"billiard", /* 1F */
"bison", /* 20 */
"blackjack", /* 21 */
"blockade", /* 22 */
"blowtorch", /* 23 */
"bluebird", /* 24 */
"bombast", /* 25 */
"bookshelf", /* 26 */
"brackish", /* 27 */
"breadline", /* 28 */
"breakup", /* 29 */
"brickyard", /* 2A */
"briefcase", /* 2B */
"Burbank", /* 2C */
"button", /* 2D */
"buzzard", /* 2E */
"cement", /* 2F */
"chairlift", /* 30 */
"chatter", /* 31 */
"checkup", /* 32 */
"chisel", /* 33 */
"choking", /* 34 */
"chopper", /* 35 */
"Christmas", /* 36 */
"clamshell", /* 37 */
"classic", /* 38 */
"classroom", /* 39 */
"cleanup", /* 3A */
"clockwork", /* 3B */
"cobra", /* 3C */
"commence", /* 3D */
"concert", /* 3E */
"cowbell", /* 3F */
"crackdown", /* 40 */
"cranky", /* 41 */
"crowfoot", /* 42 */
"crucial", /* 43 */
"crumpled", /* 44 */
"crusade", /* 45 */
"cubic", /* 46 */
"dashboard", /* 47 */
"deadbolt", /* 48 */
"deckhand", /* 49 */
"dogsled", /* 4A */
"dragnet", /* 4B */
"drainage", /* 4C */
"dreadful", /* 4D */
"drifter", /* 4E */
"dropper", /* 4F */
"drumbeat", /* 50 */
"drunken", /* 51 */
"Dupont", /* 52 */
"dwelling", /* 53 */
"eating", /* 54 */
"edict", /* 55 */
"egghead", /* 56 */
"eightball", /* 57 */
"endorse", /* 58 */
"endow", /* 59 */
"enlist", /* 5A */
"erase", /* 5B */
"escape", /* 5C */
"exceed", /* 5D */
"eyeglass", /* 5E */
"eyetooth", /* 5F */
"facial", /* 60 */
"fallout", /* 61 */
"flagpole", /* 62 */
"flatfoot", /* 63 */
"flytrap", /* 64 */
"fracture", /* 65 */
"framework", /* 66 */
"freedom", /* 67 */
"frighten", /* 68 */
"gazelle", /* 69 */
"Geiger", /* 6A */
"glitter", /* 6B */
"glucose", /* 6C */
"goggles", /* 6D */
"goldfish", /* 6E */
"gremlin", /* 6F */
"guidance", /* 70 */
"hamlet", /* 71 */
"highchair", /* 72 */
"hockey", /* 73 */
"indoors", /* 74 */
"indulge", /* 75 */
"inverse", /* 76 */
"involve", /* 77 */
"island", /* 78 */
"jawbone", /* 79 */
"keyboard", /* 7A */
"kickoff", /* 7B */
"kiwi", /* 7C */
"klaxon", /* 7D */
"locale", /* 7E */
"lockup", /* 7F */
"merit", /* 80 */
"minnow", /* 81 */
"miser", /* 82 */
"Mohawk", /* 83 */
"mural", /* 84 */
"music", /* 85 */
"necklace", /* 86 */
"Neptune", /* 87 */
"newborn", /* 88 */
"nightbird", /* 89 */
"Oakland", /* 8A */
"obtuse", /* 8B */
"offload", /* 8C */
"optic", /* 8D */
"orca", /* 8E */
"payday", /* 8F */
"peachy", /* 90 */
"pheasant", /* 91 */
"physique", /* 92 */
"playhouse", /* 93 */
"Pluto", /* 94 */
"preclude", /* 95 */
"prefer", /* 96 */
"preshrunk", /* 97 */
"printer", /* 98 */
"prowler", /* 99 */
"pupil", /* 9A */
"puppy", /* 9B */
"python", /* 9C */
"quadrant", /* 9D */
"quiver", /* 9E */
"quota", /* 9F */
"ragtime", /* A0 */
"ratchet", /* A1 */
"rebirth", /* A2 */
"reform", /* A3 */
"regain", /* A4 */
"reindeer", /* A5 */
"rematch", /* A6 */
"repay", /* A7 */
"retouch", /* A8 */
"revenge", /* A9 */
"reward", /* AA */
"rhythm", /* AB */
"ribcage", /* AC */
"ringbolt", /* AD */
"robust", /* AE */
"rocker", /* AF */
"ruffled", /* B0 */
"sailboat", /* B1 */
"sawdust", /* B2 */
"scallion", /* B3 */
"scenic", /* B4 */
"scorecard", /* B5 */
"Scotland", /* B6 */
"seabird", /* B7 */
"select", /* B8 */
"sentence", /* B9 */
"shadow", /* BA */
"shamrock", /* BB */
"showgirl", /* BC */
"skullcap", /* BD */
"skydive", /* BE */
"slingshot", /* BF */
"slowdown", /* C0 */
"snapline", /* C1 */
"snapshot", /* C2 */
"snowcap", /* C3 */
"snowslide", /* C4 */
"solo", /* C5 */
"southward", /* C6 */
"soybean", /* C7 */
"spaniel", /* C8 */
"spearhead", /* C9 */
"spellbind", /* CA */
"spheroid", /* CB */
"spigot", /* CC */
"spindle", /* CD */
"spyglass", /* CE */
"stagehand", /* CF */
"stagnate", /* D0 */
"stairway", /* D1 */
"standard", /* D2 */
"stapler", /* D3 */
"steamship", /* D4 */
"sterling", /* D5 */
"stockman", /* D6 */
"stopwatch", /* D7 */
"stormy", /* D8 */
"sugar", /* D9 */
"surmount", /* DA */
"suspense", /* DB */
"sweatband", /* DC */
"swelter", /* DD */
"tactics", /* DE */
"talon", /* DF */
"tapeworm", /* E0 */
"tempest", /* E1 */
"tiger", /* E2 */
"tissue", /* E3 */
"tonic", /* E4 */
"topmost", /* E5 */
"tracker", /* E6 */
"transit", /* E7 */
"trauma", /* E8 */
"treadmill", /* E9 */
"Trojan", /* EA */
"trouble", /* EB */
"tumor", /* EC */
"tunnel", /* ED */
"tycoon", /* EE */
"uncut", /* EF */
"unearth", /* F0 */
"unwind", /* F1 */
"uproot", /* F2 */
"upset", /* F3 */
"upshot", /* F4 */
"vapor", /* F5 */
"village", /* F6 */
"virus", /* F7 */
"Vulcan", /* F8 */
"waffle", /* F9 */
"wallet", /* FA */
"watchword", /* FB */
"wayside", /* FC */
"willow", /* FD */
"woodlark", /* FE */
"Zulu" /* FF */
};
const char * const odd[256] = {
"aardvark", /* 00 */
"absurd", /* 01 */
"accrue", /* 02 */
"acme", /* 03 */
"adrift", /* 04 */
"adult", /* 05 */
"afflict", /* 06 */
"ahead", /* 07 */
"aimless", /* 08 */
"Algol", /* 09 */
"allow", /* 0A */
"alone", /* 0B */
"ammo", /* 0C */
"ancient", /* 0D */
"apple", /* 0E */
"artist", /* 0F */
"assume", /* 10 */
"Athens", /* 11 */
"atlas", /* 12 */
"Aztec", /* 13 */
"baboon", /* 14 */
"backfield", /* 15 */
"backward", /* 16 */
"banjo", /* 17 */
"beaming", /* 18 */
"bedlamp", /* 19 */
"beehive", /* 1A */
"beeswax", /* 1B */
"befriend", /* 1C */
"Belfast", /* 1D */
"berserk", /* 1E */
"billiard", /* 1F */
"bison", /* 20 */
"blackjack", /* 21 */
"blockade", /* 22 */
"blowtorch", /* 23 */
"bluebird", /* 24 */
"bombast", /* 25 */
"bookshelf", /* 26 */
"brackish", /* 27 */
"breadline", /* 28 */
"breakup", /* 29 */
"brickyard", /* 2A */
"briefcase", /* 2B */
"Burbank", /* 2C */
"button", /* 2D */
"buzzard", /* 2E */
"cement", /* 2F */
"chairlift", /* 30 */
"chatter", /* 31 */
"checkup", /* 32 */
"chisel", /* 33 */
"choking", /* 34 */
"chopper", /* 35 */
"Christmas", /* 36 */
"clamshell", /* 37 */
"classic", /* 38 */
"classroom", /* 39 */
"cleanup", /* 3A */
"clockwork", /* 3B */
"cobra", /* 3C */
"commence", /* 3D */
"concert", /* 3E */
"cowbell", /* 3F */
"crackdown", /* 40 */
"cranky", /* 41 */
"crowfoot", /* 42 */
"crucial", /* 43 */
"crumpled", /* 44 */
"crusade", /* 45 */
"cubic", /* 46 */
"dashboard", /* 47 */
"deadbolt", /* 48 */
"deckhand", /* 49 */
"dogsled", /* 4A */
"dragnet", /* 4B */
"drainage", /* 4C */
"dreadful", /* 4D */
"drifter", /* 4E */
"dropper", /* 4F */
"drumbeat", /* 50 */
"drunken", /* 51 */
"Dupont", /* 52 */
"dwelling", /* 53 */
"eating", /* 54 */
"edict", /* 55 */
"egghead", /* 56 */
"eightball", /* 57 */
"endorse", /* 58 */
"endow", /* 59 */
"enlist", /* 5A */
"erase", /* 5B */
"escape", /* 5C */
"exceed", /* 5D */
"eyeglass", /* 5E */
"eyetooth", /* 5F */
"facial", /* 60 */
"fallout", /* 61 */
"flagpole", /* 62 */
"flatfoot", /* 63 */
"flytrap", /* 64 */
"fracture", /* 65 */
"framework", /* 66 */
"freedom", /* 67 */
"frighten", /* 68 */
"gazelle", /* 69 */
"Geiger", /* 6A */
"glitter", /* 6B */
"glucose", /* 6C */
"goggles", /* 6D */
"goldfish", /* 6E */
"gremlin", /* 6F */
"guidance", /* 70 */
"hamlet", /* 71 */
"highchair", /* 72 */
"hockey", /* 73 */
"indoors", /* 74 */
"indulge", /* 75 */
"inverse", /* 76 */
"involve", /* 77 */
"island", /* 78 */
"jawbone", /* 79 */
"keyboard", /* 7A */
"kickoff", /* 7B */
"kiwi", /* 7C */
"klaxon", /* 7D */
"locale", /* 7E */
"lockup", /* 7F */
"merit", /* 80 */
"minnow", /* 81 */
"miser", /* 82 */
"Mohawk", /* 83 */
"mural", /* 84 */
"music", /* 85 */
"necklace", /* 86 */
"Neptune", /* 87 */
"newborn", /* 88 */
"nightbird", /* 89 */
"Oakland", /* 8A */
"obtuse", /* 8B */
"offload", /* 8C */
"optic", /* 8D */
"orca", /* 8E */
"payday", /* 8F */
"peachy", /* 90 */
"pheasant", /* 91 */
"physique", /* 92 */
"playhouse", /* 93 */
"Pluto", /* 94 */
"preclude", /* 95 */
"prefer", /* 96 */
"preshrunk", /* 97 */
"printer", /* 98 */
"prowler", /* 99 */
"pupil", /* 9A */
"puppy", /* 9B */
"python", /* 9C */
"quadrant", /* 9D */
"quiver", /* 9E */
"quota", /* 9F */
"ragtime", /* A0 */
"ratchet", /* A1 */
"rebirth", /* A2 */
"reform", /* A3 */
"regain", /* A4 */
"reindeer", /* A5 */
"rematch", /* A6 */
"repay", /* A7 */
"retouch", /* A8 */
"revenge", /* A9 */
"reward", /* AA */
"rhythm", /* AB */
"ribcage", /* AC */
"ringbolt", /* AD */
"robust", /* AE */
"rocker", /* AF */
"ruffled", /* B0 */
"sailboat", /* B1 */
"sawdust", /* B2 */
"scallion", /* B3 */
"scenic", /* B4 */
"scorecard", /* B5 */
"Scotland", /* B6 */
"seabird", /* B7 */
"select", /* B8 */
"sentence", /* B9 */
"shadow", /* BA */
"shamrock", /* BB */
"showgirl", /* BC */
"skullcap", /* BD */
"skydive", /* BE */
"slingshot", /* BF */
"slowdown", /* C0 */
"snapline", /* C1 */
"snapshot", /* C2 */
"snowcap", /* C3 */
"snowslide", /* C4 */
"solo", /* C5 */
"southward", /* C6 */
"soybean", /* C7 */
"spaniel", /* C8 */
"spearhead", /* C9 */
"spellbind", /* CA */
"spheroid", /* CB */
"spigot", /* CC */
"spindle", /* CD */
"spyglass", /* CE */
"stagehand", /* CF */
"stagnate", /* D0 */
"stairway", /* D1 */
"standard", /* D2 */
"stapler", /* D3 */
"steamship", /* D4 */
"sterling", /* D5 */
"stockman", /* D6 */
"stopwatch", /* D7 */
"stormy", /* D8 */
"sugar", /* D9 */
"surmount", /* DA */
"suspense", /* DB */
"sweatband", /* DC */
"swelter", /* DD */
"tactics", /* DE */
"talon", /* DF */
"tapeworm", /* E0 */
"tempest", /* E1 */
"tiger", /* E2 */
"tissue", /* E3 */
"tonic", /* E4 */
"topmost", /* E5 */
"tracker", /* E6 */
"transit", /* E7 */
"trauma", /* E8 */
"treadmill", /* E9 */
"Trojan", /* EA */
"trouble", /* EB */
"tumor", /* EC */
"tunnel", /* ED */
"tycoon", /* EE */
"uncut", /* EF */
"unearth", /* F0 */
"unwind", /* F1 */
"uproot", /* F2 */
"upset", /* F3 */
"upshot", /* F4 */
"vapor", /* F5 */
"village", /* F6 */
"virus", /* F7 */
"Vulcan", /* F8 */
"waffle", /* F9 */
"wallet", /* FA */
"watchword", /* FB */
"wayside", /* FC */
"willow", /* FD */
"woodlark", /* FE */
"Zulu", /* FF */
};

7
fw/src/words.h Normal file
View file

@ -0,0 +1,7 @@
#ifndef __ADJECTIVES_H__
#define __ADJECTIVES_H__
extern const char * const even[256];
extern const char * const odd[256];
#endif