This refactors out a bunch of code to make better use of the SYSINIT mechanism... Completes the PKI side of things for doing mutual authentication... Uses a python script to generate the USB descriptors, and basic interface code for the STM USB HAL Layer.. This gets ride of a lot of the glue code that was previously written, allowing the glue code to be easily changed/update... break out the memory debug function into it's own file... Minor fixes to the rs485 framing code... Finally found the bug where aborting a receive would corrupt the RX state for some reason... Add back RNG seeding, and make it generic...main
@@ -18,7 +18,8 @@ These are the device that are under conideration for the project: | |||||
### STM32F103C8 | ### STM32F103C8 | ||||
64KB flash | |||||
64KB flash, but the devices I have seem to have 128KB flash. They | |||||
appear to be BluePill clones. Green LED is GPIOC Pin 13. | |||||
Currently looking at USART3, B0 for DE, B1 for RE. | Currently looking at USART3, B0 for DE, B1 for RE. | ||||
@@ -55,3 +56,64 @@ D - Driver | |||||
R - Receiver | R - Receiver | ||||
RO Receiver output | RO Receiver output | ||||
/RE Receiver enable (low enable) | /RE Receiver enable (low enable) | ||||
Note: Some pre-assembled devices have pull up resistors on the DE/RE | |||||
lines. This is a BAD design as it means that it will be driving the | |||||
bus by default. This can often be fixed by removing the pull-up | |||||
resistors. | |||||
Programming | |||||
----------- | |||||
Command to program the HID device: | |||||
``` | |||||
sudo openocd -f interface/ftdi/digilent-hs1.cfg -f interface/ftdi/swd-resistor-hack.cfg -f target/stm32f1x.cfg -c "init" -c "reset init" -c "program build/rs485hid.elf verify reset exit" | |||||
``` | |||||
Keying | |||||
------ | |||||
There are two keys involved, the initiator key, which is made w/ the | |||||
Makefile, but issuing the command: | |||||
``` | |||||
bmake hid_priv_key | |||||
``` | |||||
Which creates the private key in the file `.hid_priv_key`. This is | |||||
needed for the build process. | |||||
The other key is the device key, which is generated by the USB HID | |||||
device at first start. The device will "type" the hex encoded public | |||||
key when the A7 pin is grounded. This public key should be saved to | |||||
a file, which can then be passed to the `lora.py` initiator program | |||||
via the `-p` argument. | |||||
Low level info | |||||
-------------- | |||||
Definitions: | |||||
Mark: logic 1, B > A | |||||
Space: logic 0, B < A | |||||
Idle state: Mark | |||||
Async comms: Space | 8 bits LSB, 0 through 7 | Mark | |||||
As the start of the sequence begins w/ a space, it requires that the | |||||
line be "idle" (aka mark) before things start, so any non-mark state | |||||
before TX starts should consider the line as busy, and not be ready | |||||
to transmit. Additional info on this is in the Wiring section. | |||||
Wiring | |||||
------ | |||||
For long runs, it is recommend to have terminating resistors w/ a value | |||||
of 120 Ω, the impedence of twisted pair, to prevent reflections. | |||||
Wikipedia recommends to add biasing resistors to help w/ noise | |||||
immunity, BUT, care must be done when using them. When the bus is | |||||
idle, make sure that the receivers are outputing a hi value (aka | |||||
mark), that is 5V if you're using a MAX485 converter. If it is 0V, | |||||
then the receiver will not work. I have seen in some cases where | |||||
grounding A w/ a 2.2k Ω resistor makes things work. |
@@ -0,0 +1,41 @@ | |||||
#include <board/simpflash.h> | |||||
#include <stm32f1xx.h> | |||||
#include <stm32f1xx_hal_flash.h> | |||||
#include <strobe_rng_init.h> /* roundup */ | |||||
void | |||||
doflash(const void *dst, void *src, int bytes) | |||||
{ | |||||
FLASH_EraseInitTypeDef erase; | |||||
const uint32_t *dstp; | |||||
uint32_t *srcp; | |||||
size_t i; | |||||
uint32_t pageerr; | |||||
uint32_t primask; | |||||
dstp = dst; | |||||
srcp = src; | |||||
primask = __get_PRIMASK(); | |||||
__disable_irq(); | |||||
HAL_FLASH_Unlock(); | |||||
erase = (FLASH_EraseInitTypeDef){ | |||||
.TypeErase = FLASH_TYPEERASE_PAGES, | |||||
.Banks = FLASH_BANK_1, | |||||
.PageAddress = (intptr_t)&dstp[0], | |||||
.NbPages = roundup(bytes, FLASH_PAGE_SIZE) / FLASH_PAGE_SIZE, | |||||
}; | |||||
HAL_FLASHEx_Erase(&erase, &pageerr); | |||||
for (i = 0; i < roundup(bytes, sizeof *srcp) / 4; i++) { | |||||
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, (intptr_t)&dstp[i], srcp[i]); | |||||
} | |||||
HAL_FLASH_Lock(); | |||||
__set_PRIMASK(primask); | |||||
} |
@@ -0,0 +1,5 @@ | |||||
#include <stm32f1xx_hal.h> | |||||
#include <sysinit.h> | |||||
SYSINIT(hal_init, SI_SUB_HAL, SI_ORDER_FIRST, (void (*)(const void *))HAL_Init, NULL); |
@@ -0,0 +1 @@ | |||||
void doflash(const void *dst, void *src, int bytes); |
@@ -0,0 +1,62 @@ | |||||
/* | |||||
* More info here: | |||||
* https://stm32-base.org/boards/STM32F103C8T6-Blue-Pill.html | |||||
* | |||||
* And resistor fix: | |||||
* https://amitesh-singh.github.io/stm32/2017/05/27/Overcoming-wrong-pullup-in-blue-pill.html | |||||
* | |||||
* Note: I didn't have to do this fix, and things seem to work fine. | |||||
*/ | |||||
#include <stm32f1xx_hal_flash.h> | |||||
#include <stm32f1xx_hal_rcc.h> | |||||
#include <sysinit.h> | |||||
/* | |||||
* Referenced from: | |||||
* Projects/STM32F103RB-Nucleo/Applications/USB_Device/HID_Standalone/Src/main.c | |||||
*/ | |||||
static void | |||||
oscconfig(const void *none) | |||||
{ | |||||
RCC_ClkInitTypeDef clkinitstruct; | |||||
RCC_OscInitTypeDef oscinitstruct; | |||||
RCC_PeriphCLKInitTypeDef rccperiphclkinit; | |||||
__HAL_RCC_PWR_CLK_ENABLE(); | |||||
oscinitstruct = (RCC_OscInitTypeDef){ | |||||
.OscillatorType = RCC_OSCILLATORTYPE_HSE, | |||||
.HSEState = RCC_HSE_ON, | |||||
.HSEPredivValue = RCC_HSE_PREDIV_DIV1, | |||||
.PLL.PLLMUL = RCC_PLL_MUL9, | |||||
.PLL.PLLState = RCC_PLL_ON, | |||||
.PLL.PLLSource = RCC_PLLSOURCE_HSE, | |||||
}; | |||||
HAL_RCC_OscConfig(&oscinitstruct); | |||||
/* USB clock selection */ | |||||
rccperiphclkinit = (RCC_PeriphCLKInitTypeDef){ | |||||
.PeriphClockSelection = RCC_PERIPHCLK_USB, | |||||
.UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5, | |||||
}; | |||||
HAL_RCCEx_PeriphCLKConfig(&rccperiphclkinit); | |||||
/* | |||||
* Select PLL as system clock source and configure the HCLK, | |||||
* PCLK1 and PCLK2 clocks dividers | |||||
*/ | |||||
clkinitstruct = (RCC_ClkInitTypeDef){ | |||||
.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | | |||||
RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2), | |||||
.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK, | |||||
.AHBCLKDivider = RCC_SYSCLK_DIV1, | |||||
.APB1CLKDivider = RCC_HCLK_DIV2, | |||||
.APB2CLKDivider = RCC_HCLK_DIV1, | |||||
}; | |||||
HAL_RCC_ClockConfig(&clkinitstruct, FLASH_LATENCY_2); | |||||
} | |||||
SYSINIT(oscconfig, SI_SUB_HAL, SI_ORDER_THIRD, oscconfig, NULL); |
@@ -0,0 +1,142 @@ | |||||
/*- | |||||
* Copyright 2021 John-Mark Gurney. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
* SUCH DAMAGE. | |||||
* | |||||
*/ | |||||
#include <strobe.h> | |||||
#include <board/simpflash.h> | |||||
#include <strobe_rng_init.h> | |||||
#include "stm32f1xx.h" | |||||
#include "stm32f1xx_hal.h" | |||||
#include <stm32f1xx_hal_adc.h> | |||||
#include <sysinit.h> | |||||
#include <unistd.h> | |||||
#define NBYTESENTROPY (256 / 8) | |||||
#define DEFINE_RNG_SAVE 1 | |||||
#if DEFINE_RNG_SAVE | |||||
const rng_word_t rng_save[roundup(NBYTESENTROPY, sizeof(rng_word_t)) / sizeof(rng_word_t)] __attribute__ ((section (".savestate"))); | |||||
#endif | |||||
static void | |||||
strobe_rng_init(void) | |||||
{ | |||||
/* | |||||
* Seed RNG | |||||
* Seed w/ saved state. | |||||
*/ | |||||
strobe_seed_prng((uint8_t *)rng_save, sizeof rng_save); | |||||
/* | |||||
* On first boot, SRAM is uninitialized and randomness from | |||||
* it is used. | |||||
* | |||||
* Note: This depends upon sbrk pointing to uinitalized SRAM. | |||||
*/ | |||||
strobe_seed_prng(sbrk(0), 2*1024); | |||||
} | |||||
SYSINIT_VF(rng_init, SI_SUB_HAL, SI_ORDER_LAST, strobe_rng_init); | |||||
uint32_t times[20]; | |||||
static void | |||||
analog_seed_rng(void) | |||||
{ | |||||
ADC_HandleTypeDef ah; | |||||
ADC_ChannelConfTypeDef chanconf; | |||||
uint16_t v; | |||||
int timespos; | |||||
int i; | |||||
/* Enable ADC1 Clock */ | |||||
__HAL_RCC_ADC1_CLK_ENABLE(); | |||||
/* Configure */ | |||||
ah = (ADC_HandleTypeDef){ | |||||
.Instance = ADC1, | |||||
.Init.DataAlign = ADC_DATAALIGN_RIGHT, | |||||
.Init.ScanConvMode = ADC_SCAN_DISABLE, | |||||
.Init.ContinuousConvMode = DISABLE, | |||||
.Init.NbrOfConversion = 1, | |||||
.Init.DiscontinuousConvMode = DISABLE, | |||||
.Init.NbrOfDiscConversion = 1, | |||||
.Init.ExternalTrigConv = ADC_SOFTWARE_START, | |||||
}; | |||||
HAL_ADC_Init(&ah); | |||||
chanconf = (ADC_ChannelConfTypeDef){ | |||||
.Rank = ADC_REGULAR_RANK_1, | |||||
.Channel = ADC_CHANNEL_TEMPSENSOR, /* temp sensor */ | |||||
.SamplingTime = ADC_SAMPLETIME_71CYCLES_5, | |||||
}; | |||||
HAL_ADC_ConfigChannel(&ah, &chanconf); | |||||
HAL_Delay(1); | |||||
timespos = 0; | |||||
/* Capture */ | |||||
for (i = 0; i < 256 / 2; i++) { | |||||
times[timespos++] = HAL_GetTick(); | |||||
timespos %= sizeof times / sizeof *times; | |||||
/* | |||||
* Capture some ADC data. If pin is floating, 0xfff | |||||
* happens frequently, if pin is grounded, 0 happens | |||||
* frequently, filter these values out. | |||||
*/ | |||||
do { | |||||
HAL_ADC_Start(&ah); | |||||
HAL_ADC_PollForConversion(&ah, 1); | |||||
v = HAL_ADC_GetValue(&ah); | |||||
} while (v == 0 || v == 0xfff); | |||||
strobe_seed_prng((uint8_t *)&v, sizeof v); | |||||
} | |||||
/* Deinit */ | |||||
HAL_ADC_DeInit(&ah); | |||||
} | |||||
SYSINIT_VF(analog_seed_rng, SI_SUB_HAL, SI_ORDER_LAST, analog_seed_rng); | |||||
static void | |||||
strobe_rng_save(void) | |||||
{ | |||||
rng_word_t tmp[sizeof rng_save / sizeof(rng_word_t)]; | |||||
int r; | |||||
/* | |||||
* Save entropy for next reset. | |||||
*/ | |||||
r = strobe_randomize((uint8_t *)tmp, sizeof tmp); | |||||
(void)r; | |||||
doflash(rng_save, tmp, sizeof(rng_save)); | |||||
} | |||||
SYSINIT_VF(rng_save, SI_SUB_LAST, SI_ORDER_LAST, strobe_rng_save); |
@@ -23,10 +23,12 @@ | |||||
# | # | ||||
import asyncio | import asyncio | ||||
import codecs | |||||
import contextlib | import contextlib | ||||
import functools | import functools | ||||
import itertools | import itertools | ||||
import os | import os | ||||
import pathlib | |||||
import sys | import sys | ||||
import unittest | import unittest | ||||
@@ -49,6 +51,44 @@ CMD_PING = 4 # arg: (): a no op command | |||||
CMD_SETUNSET = 5 # arg: (chan, val): sets chan to val | CMD_SETUNSET = 5 # arg: (chan, val): sets chan to val | ||||
CMD_ADV = 6 # arg: ([cnt]): advances to the next cnt (default 1) command | CMD_ADV = 6 # arg: ([cnt]): advances to the next cnt (default 1) command | ||||
CMD_CLEAR = 7 # arg: (): clears all future commands, but keeps current running | CMD_CLEAR = 7 # arg: (): clears all future commands, but keeps current running | ||||
CMD_KEY = 8 # arg: ([key reports]): standard USB HID key reports, return is number added | |||||
_directmap = [ | |||||
(40, '\n\x1b\b\t -=[]\\#;\'`,./'), | |||||
(40, '\r'), | |||||
] | |||||
_keymap = { chr(x): x - ord('a') + 4 for x in range(ord('a'), ord('z') + 1) } | \ | |||||
{ '0': 39 } | \ | |||||
{ chr(x): x - ord('1') + 30 for x in range(ord('1'), ord('9') + 1) } | \ | |||||
{ x: i + base for base, keys in _directmap for i, x in enumerate(keys) } | |||||
_shiftmaplist = '!1@2#3$4%5^6&7*8(9)0_-+=~`{[}]|\\:;"\'<,>.?/' | |||||
_completemap = { x: (False, y) for x, y in _keymap.items() } | \ | |||||
{ x.upper(): (True, y) for x, y in _keymap.items() if x != x.upper() } | \ | |||||
{ x: (True, _keymap[y]) for x, y in zip(_shiftmaplist[::2], _shiftmaplist[1::2]) } | |||||
def makekeybuf(s): | |||||
blank = b'\x00' * 8 | |||||
# start clean | |||||
ret = [ blank ] | |||||
templ = bytearray([ 0 ] * 8) | |||||
for i in s: | |||||
shift, key = _completemap[i] | |||||
if ret[-1][2] == key: | |||||
ret.append(blank) | |||||
templ[2] = key | |||||
templ[0] = 2 if shift else 0 | |||||
ret.append(templ[:]) | |||||
# end clean | |||||
ret.append(blank) | |||||
return b''.join(ret) | |||||
class LORANode(object): | class LORANode(object): | ||||
'''Implement a LORANode initiator. | '''Implement a LORANode initiator. | ||||
@@ -140,7 +180,10 @@ class LORANode(object): | |||||
def _encodeargs(*args): | def _encodeargs(*args): | ||||
r = [] | r = [] | ||||
for i in args: | for i in args: | ||||
r.append(i.to_bytes(4, byteorder='little')) | |||||
if isinstance(i, bytes): | |||||
r.append(i) | |||||
else: | |||||
r.append(i.to_bytes(4, byteorder='little')) | |||||
return b''.join(r) | return b''.join(r) | ||||
@@ -153,6 +196,10 @@ class LORANode(object): | |||||
'response does not match, got: %s, expected: %s' % | 'response does not match, got: %s, expected: %s' % | ||||
(repr(resp[0:1]), repr(cmdbyte))) | (repr(resp[0:1]), repr(cmdbyte))) | ||||
r = resp[1:] | |||||
if r: | |||||
return r | |||||
async def waitfor(self, length): | async def waitfor(self, length): | ||||
return await self._sendcmd(CMD_WAITFOR, length) | return await self._sendcmd(CMD_WAITFOR, length) | ||||
@@ -165,6 +212,14 @@ class LORANode(object): | |||||
async def ping(self): | async def ping(self): | ||||
return await self._sendcmd(CMD_PING) | return await self._sendcmd(CMD_PING) | ||||
async def type(self, s): | |||||
keys = makekeybuf(s) | |||||
while keys: | |||||
r = await self._sendcmd(CMD_KEY, keys[:8 * 6]) | |||||
r = int.from_bytes(r, byteorder='little') | |||||
keys = keys[r * 8:] | |||||
async def adv(self, cnt=None): | async def adv(self, cnt=None): | ||||
args = () | args = () | ||||
if cnt is not None: | if cnt is not None: | ||||
@@ -267,23 +322,52 @@ async def main(): | |||||
from loraserv import DEFAULT_MADDR as maddr | from loraserv import DEFAULT_MADDR as maddr | ||||
parser = argparse.ArgumentParser() | |||||
parser = argparse.ArgumentParser(description='This is an implementation of both the server and client implementing syote secure IoT protocol.', | |||||
epilog='Both -k and -p MUST be used together. One of either -s or the combone -k & -p MUST be specified.') | |||||
parser.add_argument('-f', dest='schedfile', metavar='filename', type=str, | parser.add_argument('-f', dest='schedfile', metavar='filename', type=str, | ||||
help='Use commands from the file. One command per line.') | help='Use commands from the file. One command per line.') | ||||
parser.add_argument('-k', dest='privkey', metavar='privfile', type=str, | |||||
help='File containing a hex encoded private key.') | |||||
parser.add_argument('-p', dest='pubkey', metavar='pubfile', type=str, | |||||
help='File containing a hex encoded public key.') | |||||
parser.add_argument('-r', dest='client', metavar='module:function', type=str, | parser.add_argument('-r', dest='client', metavar='module:function', type=str, | ||||
help='Create a respondant instead of sending commands. Commands will be passed to the function.') | help='Create a respondant instead of sending commands. Commands will be passed to the function.') | ||||
parser.add_argument('-s', dest='shared_key', metavar='shared_key', type=str, required=True, | |||||
help='The shared key (encoded as UTF-8) to use.') | |||||
parser.add_argument('-s', dest='shared_key', metavar='shared_key', type=str, | |||||
help='File containing the shared key to use (note, any white space is included).') | |||||
parser.add_argument('args', metavar='CMD_ARG', type=str, nargs='*', | parser.add_argument('args', metavar='CMD_ARG', type=str, nargs='*', | ||||
help='Various commands to send to the device.') | help='Various commands to send to the device.') | ||||
args = parser.parse_args() | args = parser.parse_args() | ||||
shared_key = args.shared_key.encode('utf-8') | |||||
# make sure a key is specified. | |||||
if args.privkey is None and args.pubkey is None and \ | |||||
args.shared_key is None: | |||||
parser.error('a key must be specified (either -k and -p, or -s)') | |||||
# make sure if one is specified, both are. | |||||
if (args.privkey is not None or args.pubkey is not None) and \ | |||||
(args.privkey is None or args.pubkey is None): | |||||
parser.error('both -k and -p MUST be specified if one is.') | |||||
if args.shared_key is not None: | |||||
skdata = pathlib.Path(args.shared_key).read_bytes() | |||||
lorakwargs = dict(shared_key=skdata) | |||||
commsinitargs = (lorakwargs['shared_key'], ) | |||||
else: | |||||
privkeydata = pathlib.Path(args.privkey).read_text().strip() | |||||
privkey = X25519.frombytes(codecs.decode(privkeydata, 'hex')) | |||||
pubkeydata = pathlib.Path(args.pubkey).read_text().strip() | |||||
pubkey = codecs.decode(pubkeydata, 'hex') | |||||
lorakwargs = dict(init_key=privkey, resp_pub=pubkey) | |||||
commsinitargs = (None, make_pktbuf(privkey.getpriv()), | |||||
make_pktbuf(pubkey)) | |||||
if args.client: | if args.client: | ||||
# Run a client | # Run a client | ||||
mr = await multicast.create_multicast_receiver(maddr) | mr = await multicast.create_multicast_receiver(maddr) | ||||
mt = await multicast.create_multicast_transmitter(maddr) | mt = await multicast.create_multicast_transmitter(maddr) | ||||
@@ -313,16 +397,20 @@ async def main(): | |||||
cb = syote_comms.process_msgfunc_t(client_call) | cb = syote_comms.process_msgfunc_t(client_call) | ||||
# Initialize everything | # Initialize everything | ||||
syote_comms.comms_init(commstate, cb, make_pktbuf(shared_key)) | |||||
syote_comms.comms_init(commstate, cb, *commsinitargs) | |||||
try: | try: | ||||
while True: | while True: | ||||
pkt = await mr.recv() | pkt = await mr.recv() | ||||
msg = pkt[0] | msg = pkt[0] | ||||
#_debprint('procmsg:', repr(msg)) | |||||
out = syote_comms.comms_process_wrap( | out = syote_comms.comms_process_wrap( | ||||
commstate, msg) | commstate, msg) | ||||
#_debprint('resp:', repr(out)) | |||||
if out: | if out: | ||||
await mt.send(out) | await mt.send(out) | ||||
finally: | finally: | ||||
@@ -333,13 +421,13 @@ async def main(): | |||||
msd = MulticastSyncDatagram(maddr) | msd = MulticastSyncDatagram(maddr) | ||||
await msd.start() | await msd.start() | ||||
l = LORANode(msd, shared=shared_key) | |||||
l = LORANode(msd, **lorakwargs) | |||||
await l.start() | await l.start() | ||||
valid_cmds = { | valid_cmds = { | ||||
'waitfor', 'setunset', 'runfor', 'ping', 'adv', 'clear', | 'waitfor', 'setunset', 'runfor', 'ping', 'adv', 'clear', | ||||
'terminate', | |||||
'terminate', 'type', | |||||
} | } | ||||
if args.args and args.schedfile: | if args.args and args.schedfile: | ||||
@@ -364,7 +452,10 @@ async def main(): | |||||
fun = getattr(l, cmd) | fun = getattr(l, cmd) | ||||
await fun(*(int(x) for x in args)) | |||||
try: | |||||
await fun(*(int(x) for x in args)) | |||||
except: | |||||
await fun(*args) | |||||
if __name__ == '__main__': | if __name__ == '__main__': | ||||
asyncio.run(main()) | asyncio.run(main()) | ||||
@@ -1096,7 +1187,7 @@ class TestLORANode(unittest.IsolatedAsyncioTestCase): | |||||
@timeout(2) | @timeout(2) | ||||
async def test_ccode(self): | async def test_ccode(self): | ||||
_self = self | _self = self | ||||
from ctypes import c_uint8 | |||||
from ctypes import c_uint8, memmove | |||||
# seed the RNG | # seed the RNG | ||||
prngseed = b'abc123' | prngseed = b'abc123' | ||||
@@ -1111,28 +1202,40 @@ class TestLORANode(unittest.IsolatedAsyncioTestCase): | |||||
(CMD_WAITFOR, [ 30 ]), | (CMD_WAITFOR, [ 30 ]), | ||||
(CMD_RUNFOR, [ 1, 50 ]), | (CMD_RUNFOR, [ 1, 50 ]), | ||||
(CMD_PING, [ ]), | (CMD_PING, [ ]), | ||||
# using big here, because Python is stupid. | |||||
(CMD_KEY, b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00\x00\x00\x00\x02\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x17\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', CMD_KEY.to_bytes(1, byteorder='big') + (3).to_bytes(4, byteorder='little')), | |||||
(CMD_KEY, b'\x00\x00\x17\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', CMD_KEY.to_bytes(1, byteorder='big') + (2).to_bytes(4, byteorder='little')), | |||||
(CMD_TERMINATE, [ ]), | (CMD_TERMINATE, [ ]), | ||||
] | ] | ||||
def procmsg(msg, outbuf): | def procmsg(msg, outbuf): | ||||
msgbuf = msg._from() | msgbuf = msg._from() | ||||
cmd = msgbuf[0] | cmd = msgbuf[0] | ||||
args = [ int.from_bytes(msgbuf[x:x + 4], | |||||
byteorder='little') for x in range(1, len(msgbuf), | |||||
4) ] | |||||
if isinstance(exptmsgs[0][1], bytes): | |||||
args = msgbuf[1:] | |||||
else: | |||||
args = [ int.from_bytes(msgbuf[x:x + 4], | |||||
byteorder='little') for x in range(1, len(msgbuf), | |||||
4) ] | |||||
if exptmsgs[0][:2] == (cmd, args): | |||||
if len(exptmsgs[0]) > 2: | |||||
rmsg = exptmsgs[0][2] | |||||
memmove(outbuf[0].pkt, rmsg, len(rmsg)) | |||||
outbuf[0].pktlen = len(rmsg) | |||||
else: | |||||
outbuf[0].pkt[0] = cmd | |||||
outbuf[0].pktlen = 1 | |||||
if exptmsgs[0] == (cmd, args): | |||||
exptmsgs.pop(0) | exptmsgs.pop(0) | ||||
outbuf[0].pkt[0] = cmd | |||||
outbuf[0].pktlen = 1 | |||||
else: #pragma: no cover | else: #pragma: no cover | ||||
raise RuntimeError('cmd not found') | |||||
raise RuntimeError('cmd not found, got %d, expected %d' % (cmd, exptmsgs[0][0])) | |||||
# wrap the callback function | # wrap the callback function | ||||
cb = syote_comms.process_msgfunc_t(procmsg) | cb = syote_comms.process_msgfunc_t(procmsg) | ||||
class CCodeSD(MockSyncDatagram): | class CCodeSD(MockSyncDatagram): | ||||
async def runner(self): | async def runner(self): | ||||
for expectlen in [ 24, 17, 9, 9, 9, 9 ]: | |||||
for expectlen in [ 24, 17, 9, 9, 9, 13, 13, 9 ]: | |||||
# get message | # get message | ||||
inmsg = await self.get() | inmsg = await self.get() | ||||
@@ -1176,6 +1279,8 @@ class TestLORANode(unittest.IsolatedAsyncioTestCase): | |||||
await l.ping() | await l.ping() | ||||
await l.type('sHt') | |||||
await l.terminate() | await l.terminate() | ||||
await tsd.drain() | await tsd.drain() | ||||
@@ -16,8 +16,14 @@ CFLAGS+= -Wall -Werror | |||||
.include <$(.PARSEDIR)/mu.opts.mk> | .include <$(.PARSEDIR)/mu.opts.mk> | ||||
.if ${MK_HAL_INIT} == "yes" | |||||
.PATH: $(SRCTOP)/board | |||||
SRCS+= hal_init.c | |||||
.endif | |||||
.if ${MK_SYSINIT} == "yes" | .if ${MK_SYSINIT} == "yes" | ||||
.PATH: $(SRCTOP) | .PATH: $(SRCTOP) | ||||
CFLAGS+= -I$(SRCTOP) | |||||
SRCS+= sysinit.c | SRCS+= sysinit.c | ||||
.endif | .endif | ||||
@@ -29,6 +35,11 @@ STROBE_SRCS+= strobe.c \ | |||||
x25519.c | x25519.c | ||||
.endif | .endif | ||||
.if ${MK_STROBE} == "yes" && ${MK_STM32F103} == "yes" | |||||
.PATH: $(SRCTOP)/board | |||||
STROBE_SRCS+= strobe_f1_rng_init.c | |||||
.endif | |||||
# LoRamac (SX1276) radio code | # LoRamac (SX1276) radio code | ||||
.if ${MK_SX1276} == "yes" | .if ${MK_SX1276} == "yes" | ||||
LORAMAC_SRC = $(SRCTOP)/loramac/src | LORAMAC_SRC = $(SRCTOP)/loramac/src | ||||
@@ -49,15 +60,21 @@ SRCS+= adc-board.c delay-board.c gpio-board.c rtc-board.c lpm-board.c sx1276mb1l | |||||
ARMTARGET?= -mcpu=cortex-m3 -mthumb | ARMTARGET?= -mcpu=cortex-m3 -mthumb | ||||
.PATH: $(STM32)/f103c8t6 | .PATH: $(STM32)/f103c8t6 | ||||
.PATH: $(SRCTOP)/board | |||||
LINKER_SCRIPT=$(STM32)/f103c8t6/STM32F103C8T6_FLASH.ld | LINKER_SCRIPT=$(STM32)/f103c8t6/STM32F103C8T6_FLASH.ld | ||||
SRCS+= \ | SRCS+= \ | ||||
misc.c \ | |||||
f1_flash.c \ | |||||
hal_generic.c \ | hal_generic.c \ | ||||
startup_stm32f103xb.s \ | startup_stm32f103xb.s \ | ||||
stm32_bluepill.c \ | |||||
stm32f1xx_hal.c \ | stm32f1xx_hal.c \ | ||||
stm32f1xx_hal_adc.c \ | |||||
stm32f1xx_hal_adc_ex.c \ | |||||
stm32f1xx_hal_cortex.c \ | stm32f1xx_hal_cortex.c \ | ||||
stm32f1xx_hal_dma.c \ | stm32f1xx_hal_dma.c \ | ||||
stm32f1xx_hal_flash.c \ | |||||
stm32f1xx_hal_flash_ex.c \ | |||||
stm32f1xx_hal_gpio.c \ | stm32f1xx_hal_gpio.c \ | ||||
stm32f1xx_hal_pcd.c \ | stm32f1xx_hal_pcd.c \ | ||||
stm32f1xx_hal_pcd_ex.c \ | stm32f1xx_hal_pcd_ex.c \ | ||||
@@ -65,7 +82,6 @@ SRCS+= \ | |||||
stm32f1xx_hal_rcc.c \ | stm32f1xx_hal_rcc.c \ | ||||
stm32f1xx_hal_rcc_ex.c \ | stm32f1xx_hal_rcc_ex.c \ | ||||
stm32f1xx_hal_uart.c \ | stm32f1xx_hal_uart.c \ | ||||
stm32f1xx_ll_usb.c \ | |||||
system_stm32f1xx.c | system_stm32f1xx.c | ||||
CFLAGS+= -I$(STM32) | CFLAGS+= -I$(STM32) | ||||
@@ -123,8 +139,8 @@ CFLAGS+= -I$(SRCTOP)/rs485hid | |||||
# USB CDC | # USB CDC | ||||
.if ${MK_USB_CDC} == "yes" | .if ${MK_USB_CDC} == "yes" | ||||
.PATH: $(STM32)/usb | .PATH: $(STM32)/usb | ||||
SRCS+= \ | |||||
si_usb.c \ | |||||
SRCS.USB_CDC+= \ | |||||
misc.c \ | |||||
usb_device.c \ | usb_device.c \ | ||||
usbd_cdc.c \ | usbd_cdc.c \ | ||||
usbd_cdc_if.c \ | usbd_cdc_if.c \ | ||||
@@ -137,7 +153,28 @@ SRCS+= \ | |||||
CFLAGS+= -I$(STM32)/usb | CFLAGS+= -I$(STM32)/usb | ||||
.endif | .endif | ||||
.if ${MK_USB_CDC} == "yes" && ${MK_NODE151} == "yes" | |||||
SRCS+= \ | |||||
# USB HID | |||||
.if ${MK_USB_HID} == "yes" | |||||
.PATH: $(STM32)/usb | |||||
CFLAGS+= -I$(.OBJDIR) | |||||
SRCS.USB_HID+= \ | |||||
usb_hid_def.c \ | |||||
usbd_conf.c \ | |||||
usbd_core.c \ | |||||
usbd_ctlreq_nodesc.c \ | |||||
usbd_ioreq.c | |||||
CFLAGS+= -I$(STM32)/usb | |||||
.endif | |||||
.if ${MK_USB} == "yes" && ${MK_STM32F103} == "yes" | |||||
SRCS.USB+= \ | |||||
si_usb.c \ | |||||
stm32f1xx_ll_usb.c | |||||
.endif | |||||
.if ${MK_USB} == "yes" && ${MK_NODE151} == "yes" | |||||
SRCS.USB_CDC+= \ | |||||
si_usb.c \ | |||||
stm32l1xx_ll_usb.c | stm32l1xx_ll_usb.c | ||||
.endif | .endif |
@@ -2,6 +2,32 @@ | |||||
# code. | # code. | ||||
# | # | ||||
# See bsd.mkopt.mk for more information. | # See bsd.mkopt.mk for more information. | ||||
# | |||||
# An option should only be listed in one of the following sections. | |||||
# If it's listed as a dependent option, do NOT list it in a default | |||||
# section. | |||||
# | |||||
# | |||||
# If the option doesn't state if it defines a SRCS var, then | |||||
# the necessary sources are added to the SRCS var. | |||||
# | |||||
# Options (by order in this file): | |||||
# | |||||
# STROBE - The STROBE crypto library. (Defines STROBE_SRCS) | |||||
# SYSINIT - The sysinit framework. Needed to run the init code. | |||||
# | |||||
# NODE151 - Code needed for the Node151 LoRa module | |||||
# RS485FRAME - Base code for RS485 framing tx/rx | |||||
# STM32F103 - Base code for the BluePill microcontroller module | |||||
# SX1276 - LoRa radio code, for Node151 | |||||
# USB_CDC - Code implementing the CDC Device mode. (Defines SRCS.USB_CDC) | |||||
# USB_HID - Code implementing the HID Device mode. (Defines SRCS.USB_HID) | |||||
# | |||||
# Dependent options: | |||||
# HAL_INIT - Run hal_init via sysinit. | |||||
# USB - Turn on generic USB support code. (Defines SRCS.USB) | |||||
# | |||||
__DEFAULT_YES_OPTIONS = STROBE \ | __DEFAULT_YES_OPTIONS = STROBE \ | ||||
SYSINIT | SYSINIT | ||||
@@ -11,8 +37,12 @@ __DEFAULT_NO_OPTIONS = \ | |||||
RS485FRAME \ | RS485FRAME \ | ||||
STM32F103 \ | STM32F103 \ | ||||
SX1276 \ | SX1276 \ | ||||
USB_CDC | |||||
USB_CDC \ | |||||
USB_HID | |||||
__DEFAULT_DEPENDENT_OPTIONS = | |||||
__DEFAULT_DEPENDENT_OPTIONS = \ | |||||
HAL_INIT/STM32F103 \ | |||||
USB/USB_CDC \ | |||||
USB/USB_HID | |||||
.include <$(.PARSEDIR)/bsd.mkopt.mk> | .include <$(.PARSEDIR)/bsd.mkopt.mk> |
@@ -31,20 +31,14 @@ | |||||
PROGEXT = .elf | PROGEXT = .elf | ||||
DEPENDS += .arm_deps | |||||
.for i in $(PROGS) | .for i in $(PROGS) | ||||
ALLTGTS+= $(i)$(PROGEXT) $(i).list | ALLTGTS+= $(i)$(PROGEXT) $(i).list | ||||
ASRCS.$(i) = $(SRCS) $(SRCS.$(i)) | ASRCS.$(i) = $(SRCS) $(SRCS.$(i)) | ||||
OBJS.$(i) = $(ASRCS.$(i):C/.c$/.o/) | OBJS.$(i) = $(ASRCS.$(i):C/.c$/.o/) | ||||
DEPENDS += .arm_deps | |||||
.arm_deps: $(ASRCS.$(i)) | |||||
.arm_deps: | |||||
$(ARMCC) $(ARMTARGET) $(CFLAGS) $(.ALLSRC) -MM > $@ || (rm -f $@ && false) | |||||
.PHONY: depend | |||||
depend: $(DEPENDS) | |||||
ARM_DEP_SRCS+= $(ASRCS.$(i)) | |||||
$(i)$(PROGEXT) $(i).map: $(OBJS.$(i)) | $(i)$(PROGEXT) $(i).map: $(OBJS.$(i)) | ||||
$(ARMCC) $(ARMTARGET) -o $(i)$(PROGEXT) $(.ALLSRC) -T$(LINKER_SCRIPT) --specs=nosys.specs -Wl,-Map="$(i).map" -Wl,--gc-sections -static --specs=nano.specs -Wl,--start-group -lc -lm -Wl,--end-group | $(ARMCC) $(ARMTARGET) -o $(i)$(PROGEXT) $(.ALLSRC) -T$(LINKER_SCRIPT) --specs=nosys.specs -Wl,-Map="$(i).map" -Wl,--gc-sections -static --specs=nano.specs -Wl,--start-group -lc -lm -Wl,--end-group | ||||
@@ -53,6 +47,14 @@ $(i).list: $(i)$(PROGEXT) | |||||
$(ARMOBJDUMP) -h -S $(.ALLSRC) > $@ || (rm -f $@ && false) | $(ARMOBJDUMP) -h -S $(.ALLSRC) > $@ || (rm -f $@ && false) | ||||
.endfor | .endfor | ||||
.PHONY: depend | |||||
depend: $(DEPENDS) | |||||
.arm_deps: $(ARM_DEP_SRCS) | |||||
.arm_deps: | |||||
$(ARMCC) $(ARMTARGET) $(CFLAGS) $(.ALLSRC) -MM > $@ || (rm -f $@ && false) | |||||
.for i in $(DEPENDS) | .for i in $(DEPENDS) | ||||
.sinclude "$i" | .sinclude "$i" | ||||
.endfor | .endfor | ||||
@@ -61,7 +63,7 @@ all: $(ALLTGTS) | |||||
.PHONY: runbuild | .PHONY: runbuild | ||||
runbuild: $(SRCS) Makefile mk/*.mk | runbuild: $(SRCS) Makefile mk/*.mk | ||||
for i in $(.MAKEFILE_LIST) $(.ALLSRC) $$(cat $(DEPENDS) | gsed ':x; /\\$$/ { N; s/\\\n//; tx }' | sed -e 's/^[^:]*://'); do if [ "$$i" != ".." ]; then echo $$i; fi; done | sort -u | entr -d sh -c 'echo starting...; cd $(.CURDIR) && $(MAKE) $(.MAKEFLAGS) depend && $(MAKE) $(.MAKEFLAGS) all' | |||||
for i in $(EXTRA_DEPENDS) $(.MAKEFILE_LIST) $(.ALLSRC) $$(cat $(DEPENDS) | gsed ':x; /\\$$/ { N; s/\\\n//; tx }' | sed -e 's/^[^:]*://'); do if [ "$$i" != ".." ]; then echo $$i; fi; done | sort -u | entr -d sh -c 'echo starting...; cd $(.CURDIR) && $(MAKE) $(.MAKEFLAGS) depend && $(MAKE) $(.MAKEFLAGS) all' | |||||
# native objects | # native objects | ||||
.SUFFIXES: .no | .SUFFIXES: .no | ||||
@@ -1,2 +1,3 @@ | |||||
coverage | coverage | ||||
strobe/python | strobe/python | ||||
git+https://www.funkthat.com/gitea/jmg/python-usb-protocol.git |
@@ -22,20 +22,64 @@ | |||||
# SUCH DAMAGE. | # SUCH DAMAGE. | ||||
# | # | ||||
PROGS = rs485gw # lora.irr | |||||
PROGS = rs485gw rs485hid | |||||
SRCS.rs485gw = rs485gw.c | SRCS.rs485gw = rs485gw.c | ||||
SRCS.rs485gw+= misc.c | |||||
SRCS.rs485gw+= $(SRCS.USB_CDC) | |||||
SRCS.rs485gw+= $(SRCS.USB) | |||||
SRCS.rs485hid = rs485hid.c | |||||
SRCS.rs485hid+= $(SRCS.USB_HID) | |||||
SRCS.rs485hid+= $(SRCS.USB) | |||||
SRCS.rs485hid+= $(STROBE_SRCS) | |||||
SRCS.rs485hid+= comms.c | |||||
SRCS.rs485hid+= memdebug.c | |||||
SRCS.rs485hid+= strobe_pki.c | |||||
CFLAGS.rs485hid+= -DEXTERNAL_GET_DESCRIPTOR=1 | |||||
CFLAGS.rs485hid+= -I$(.OBJDIR) # for public_key.h | |||||
SRCS.rs485hid+= usb_hid_base.c | |||||
.if empty(HID_PRIV_KEY) && exists($(.CURDIR)/.hid_priv_key) | |||||
HID_PRIV_KEY!= cat $(.CURDIR)/.hid_priv_key | |||||
.endif | |||||
.PHONY: hid_priv_key | |||||
hid_priv_key: | |||||
@LANG=C tr -c -d a-f0-9 < /dev/urandom | dd bs=1 of=$(.CURDIR)/.hid_priv_key count=64 2>/dev/null | |||||
@echo 'Key created and put into .hid_priv_key.' | |||||
# make this a phony target so it's always run | |||||
# dependancies will only be made when it's updated | |||||
.PHONY: $(.OBJDIR)/public_key.h | |||||
$(.OBJDIR)/public_key.h: | |||||
@if [ "$(HID_PRIV_KEY)" = "" ]; then echo 'Must provide HID_PRIV_KEY make variable or have a non-empty file ".hid_priv_key". This can be created by the command "$(MAKE) hid_priv_key".'; false; fi | |||||
@echo 'static const uint8_t pubkey[] = {' $$(python3 -c 'import sys; import codecs; from syote_comms import X25519; print(", ".join(hex(x) for x in X25519.frombytes(codecs.decode(sys.argv[1], "hex")).getpub()))' $(HID_PRIV_KEY) ) "};" > public_key.h.tmp | |||||
@echo 'static_assert(sizeof pubkey == 32);' >> public_key.h.tmp | |||||
if [ "$$(cat public_key.h.tmp)" = "static uint8_t pubkey[] = { };" -o "$$(cat public_key.h.tmp)" = "" ]; then rm -f "$@"; false; fi | |||||
(cmp public_key.h.tmp public_key.h >/dev/null 2>&1 && rm public_key.h.tmp) || mv public_key.h.tmp public_key.h | |||||
strobe_pki.o: $(.OBJDIR)/public_key.h | |||||
usb_hid_base.c: usb_hid.py | |||||
PYTHONPATH=$(.CURDIR) python $(STM32)/usb/usb_gen.py $(.ALLSRC:[1]:T:R) | |||||
.PATH: $(.CURDIR)/.. | .PATH: $(.CURDIR)/.. | ||||
CFLAGS += -g | CFLAGS += -g | ||||
CFLAGS += -I$(.CURDIR)/.. | |||||
WITH_RS485FRAME = yes | |||||
WITH_STM32F103 = yes | WITH_STM32F103 = yes | ||||
WITH_USB_CDC = yes | WITH_USB_CDC = yes | ||||
WITH_RS485FRAME = yes | |||||
WITH_USB_HID = yes | |||||
.include <../mk/boards.mk> | .include <../mk/boards.mk> | ||||
EXTRA_DEPENDS+= $(STM32)/usb/usb_gen.py | |||||
.include <../mk/mu.progs.mk> | .include <../mk/mu.progs.mk> | ||||
usb_hid_base.c: $(STM32)/usb/usb_gen.py |
@@ -0,0 +1,65 @@ | |||||
/*- | |||||
* Copyright 2021 John-Mark Gurney. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
* SUCH DAMAGE. | |||||
* | |||||
*/ | |||||
#include <misc.h> | |||||
#include <sys/param.h> | |||||
#include <stdarg.h> | |||||
#include <stdint.h> | |||||
#include <stdio.h> | |||||
#include <string.h> | |||||
#if MEM_DEBUG_BUF | |||||
static char debugbuf[1024]; | |||||
static uint32_t debugpos = 0; | |||||
void | |||||
debug_printf(const char *format, ...) | |||||
{ | |||||
char buf[128]; | |||||
char *pos; | |||||
va_list args; | |||||
uint32_t length; | |||||
uint32_t cpy; | |||||
va_start(args, format); | |||||
length = vsnprintf(buf, sizeof buf, format, args); | |||||
va_end(args); | |||||
pos = &buf[0]; | |||||
while (length) { | |||||
cpy = MIN(length, sizeof debugbuf - debugpos); | |||||
memcpy(&debugbuf[debugpos], pos, cpy); | |||||
debugpos += cpy; | |||||
if (debugpos >= sizeof debugbuf) | |||||
debugpos = 0; | |||||
pos += cpy; | |||||
length -= cpy; | |||||
} | |||||
} | |||||
#endif |
@@ -18,7 +18,7 @@ | |||||
const char *rs485err = NULL; | const char *rs485err = NULL; | ||||
static uint8_t rxbuf[1024]; | |||||
static uint8_t rxbuf[128]; | |||||
static txdone_fn_t txdone; | static txdone_fn_t txdone; | ||||
static rxdone_fn_t rxdone; | static rxdone_fn_t rxdone; | ||||
static txdone_fn_t errcb; | static txdone_fn_t errcb; | ||||
@@ -62,7 +62,7 @@ settx(bool tx) | |||||
else | else | ||||
s = GPIO_PIN_RESET; | s = GPIO_PIN_RESET; | ||||
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, s); | |||||
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, s); /* XXX - led */ | |||||
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, s); | HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, s); | ||||
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, s); | HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, s); | ||||
} | } | ||||
@@ -71,11 +71,19 @@ void | |||||
rs485_starttx(uint8_t *pkt, size_t len) | rs485_starttx(uint8_t *pkt, size_t len) | ||||
{ | { | ||||
/* XXX - make sure not tx'ing */ | |||||
/* XXX - make sure not actively rx'ing */ | |||||
/* XXX - make sure line is idle */ | /* XXX - make sure line is idle */ | ||||
if (rs485uart.RxState == HAL_UART_STATE_BUSY_RX) { | |||||
while (rs485uart.RxXferCount != sizeof rxbuf) { | |||||
} | |||||
} | |||||
#if 0 | |||||
/* XXX - as it's full duplex, this shouldn't be needed */ | |||||
/* This appears to corrupt state and break things */ | |||||
if (doingrx) { | if (doingrx) { | ||||
HAL_UART_AbortReceive_IT(&rs485uart); | HAL_UART_AbortReceive_IT(&rs485uart); | ||||
} | } | ||||
#endif | |||||
settx(true); | settx(true); | ||||
@@ -99,6 +107,8 @@ HAL_UART_TxCpltCallback(UART_HandleTypeDef *phuart) | |||||
if (phuart != &rs485uart || txdone == NULL) | if (phuart != &rs485uart || txdone == NULL) | ||||
return; | return; | ||||
for (int x = 0; x < 1000000; x++); | |||||
settx(false); | settx(false); | ||||
doingtx = 0; | doingtx = 0; | ||||
@@ -140,12 +150,13 @@ HAL_UART_MspInit(UART_HandleTypeDef *huart) | |||||
/* UM1850 § 38.1.2 Page 550 */ | /* UM1850 § 38.1.2 Page 550 */ | ||||
/* 2.a */ | /* 2.a */ | ||||
__HAL_RCC_USART3_FORCE_RESET( ); | |||||
__HAL_RCC_USART3_RELEASE_RESET( ); | |||||
__HAL_RCC_USART3_FORCE_RESET(); | |||||
__HAL_RCC_USART3_RELEASE_RESET(); | |||||
__HAL_RCC_USART3_CLK_ENABLE(); | __HAL_RCC_USART3_CLK_ENABLE(); | ||||
/* 2.b */ | /* 2.b */ | ||||
__HAL_RCC_GPIOB_CLK_ENABLE(); | __HAL_RCC_GPIOB_CLK_ENABLE(); | ||||
/* TX pin */ | /* TX pin */ | ||||
GPIO_InitStruct = (GPIO_InitTypeDef){ | GPIO_InitStruct = (GPIO_InitTypeDef){ | ||||
.Pin = GPIO_PIN_10, | .Pin = GPIO_PIN_10, | ||||
@@ -197,10 +208,17 @@ rs485_startrx() | |||||
return 0; | return 0; | ||||
} | } | ||||
doingrx = 1; | |||||
r = HAL_UARTEx_ReceiveToIdle_IT(&rs485uart, rxbuf, sizeof rxbuf); | r = HAL_UARTEx_ReceiveToIdle_IT(&rs485uart, rxbuf, sizeof rxbuf); | ||||
if (r == HAL_BUSY) { | if (r == HAL_BUSY) { | ||||
rs485err = "already"; | |||||
return 1; | |||||
#if 0 | |||||
/* This appears to corrupt state and break things */ | |||||
HAL_UART_AbortReceive_IT(&rs485uart); | HAL_UART_AbortReceive_IT(&rs485uart); | ||||
r = HAL_UARTEx_ReceiveToIdle_IT(&rs485uart, rxbuf, sizeof rxbuf); | r = HAL_UARTEx_ReceiveToIdle_IT(&rs485uart, rxbuf, sizeof rxbuf); | ||||
#endif | |||||
} | } | ||||
rs485err = statustostr(r); | rs485err = statustostr(r); | ||||
@@ -209,14 +227,16 @@ rs485_startrx() | |||||
} | } | ||||
static void | static void | ||||
rs485frame_init(const void *v) | |||||
rs485frame_init(void) | |||||
{ | { | ||||
GPIO_InitTypeDef GPIO_InitStruct; | GPIO_InitTypeDef GPIO_InitStruct; | ||||
__HAL_RCC_GPIOB_CLK_ENABLE(); | |||||
/* setup DE/RE */ | /* setup DE/RE */ | ||||
GPIO_InitStruct = (GPIO_InitTypeDef){ | GPIO_InitStruct = (GPIO_InitTypeDef){ | ||||
.Pin = GPIO_PIN_0|GPIO_PIN_1, | .Pin = GPIO_PIN_0|GPIO_PIN_1, | ||||
.Mode = GPIO_MODE_OUTPUT_OD, | |||||
.Mode = GPIO_MODE_OUTPUT_PP, | |||||
.Pull = GPIO_NOPULL, | .Pull = GPIO_NOPULL, | ||||
.Speed = GPIO_SPEED_FREQ_LOW, /* 2 MHz */ | .Speed = GPIO_SPEED_FREQ_LOW, /* 2 MHz */ | ||||
}; | }; | ||||
@@ -240,4 +260,4 @@ rs485frame_init(const void *v) | |||||
/* 4 */ | /* 4 */ | ||||
HAL_UART_Init(&rs485uart); | HAL_UART_Init(&rs485uart); | ||||
} | } | ||||
SYSINIT(rs485frame_init, SI_SUB_STANDARD, SI_ORDER_FIRST, rs485frame_init, NULL); | |||||
SYSINIT_VF(rs485frame_init, SI_SUB_STANDARD, SI_ORDER_FIRST, rs485frame_init); |
@@ -36,7 +36,18 @@ | |||||
#include <sysinit.h> | #include <sysinit.h> | ||||
SYSINIT(hal_init, SI_SUB_HAL, SI_ORDER_FIRST, (void (*)(const void *))HAL_Init, NULL); | |||||
#include <usb_device.h> | |||||
SYSINIT_VF(usb_cdc, SI_SUB_USB, SI_ORDER_MIDDLE, MX_USB_DEVICE_Init); | |||||
/* XXX - where's a better place? */ | |||||
extern PCD_HandleTypeDef hpcd_USB_FS; | |||||
void | |||||
USB_LP_IRQHandler(void) | |||||
{ | |||||
HAL_PCD_IRQHandler(&hpcd_USB_FS); | |||||
} | |||||
static int dorx = 0; | static int dorx = 0; | ||||
static uint8_t rxbuf[128]; | static uint8_t rxbuf[128]; | ||||
@@ -71,51 +82,6 @@ SYSINIT(c13led, SI_SUB_HAL, SI_ORDER_SECOND, c13led, NULL); | |||||
#define usb_printf debug_printf | #define usb_printf debug_printf | ||||
#endif | #endif | ||||
/* | |||||
* Referenced from: | |||||
* Projects/STM32F103RB-Nucleo/Applications/USB_Device/HID_Standalone/Src/main.c | |||||
*/ | |||||
static void | |||||
oscconfig(const void *none) | |||||
{ | |||||
RCC_ClkInitTypeDef clkinitstruct; | |||||
RCC_OscInitTypeDef oscinitstruct; | |||||
RCC_PeriphCLKInitTypeDef rccperiphclkinit; | |||||
__HAL_RCC_PWR_CLK_ENABLE(); | |||||
oscinitstruct = (RCC_OscInitTypeDef){ | |||||
.OscillatorType = RCC_OSCILLATORTYPE_HSE, | |||||
.HSEState = RCC_HSE_ON, | |||||
.HSEPredivValue = RCC_HSE_PREDIV_DIV1, | |||||
.PLL.PLLMUL = RCC_PLL_MUL9, | |||||
.PLL.PLLState = RCC_PLL_ON, | |||||
.PLL.PLLSource = RCC_PLLSOURCE_HSE, | |||||
}; | |||||
HAL_RCC_OscConfig(&oscinitstruct); | |||||
/* USB clock selection */ | |||||
rccperiphclkinit = (RCC_PeriphCLKInitTypeDef){ | |||||
.PeriphClockSelection = RCC_PERIPHCLK_USB, | |||||
.UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5, | |||||
}; | |||||
HAL_RCCEx_PeriphCLKConfig(&rccperiphclkinit); | |||||
/* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 | |||||
clocks dividers */ | |||||
clkinitstruct = (RCC_ClkInitTypeDef){ | |||||
.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2), | |||||
.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK, | |||||
.AHBCLKDivider = RCC_SYSCLK_DIV1, | |||||
.APB1CLKDivider = RCC_HCLK_DIV2, | |||||
.APB2CLKDivider = RCC_HCLK_DIV1, | |||||
}; | |||||
HAL_RCC_ClockConfig(&clkinitstruct, FLASH_LATENCY_2); | |||||
} | |||||
SYSINIT(oscconfig, SI_SUB_HAL, SI_ORDER_THIRD, oscconfig, NULL); | |||||
char * | char * | ||||
findeol(char *pos, size_t len) | findeol(char *pos, size_t len) | ||||
{ | { | ||||
@@ -167,19 +133,6 @@ rxdone(const uint8_t *payload, size_t size) | |||||
gotrxdone = 1; | gotrxdone = 1; | ||||
} | } | ||||
void | |||||
rxtimeout(void) | |||||
{ | |||||
usb_printf("rxtimeout\r\n"); | |||||
} | |||||
void | |||||
rxerr(void) | |||||
{ | |||||
usb_printf("rxerr\r\n"); | |||||
} | |||||
static uint8_t | static uint8_t | ||||
hexchartonib(char s) | hexchartonib(char s) | ||||
{ | { | ||||
@@ -278,7 +231,7 @@ main(void) | |||||
debug_printf("starting...\n"); | debug_printf("starting...\n"); | ||||
#if 1 | |||||
#if 0 | |||||
int i; | int i; | ||||
for (i = 0; i < 5; i++) { | for (i = 0; i < 5; i++) { | ||||
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); | HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); | ||||
@@ -0,0 +1,558 @@ | |||||
/*- | |||||
* Copyright 2021 John-Mark Gurney. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
* SUCH DAMAGE. | |||||
* | |||||
*/ | |||||
#include "usb_hid_def.h" | |||||
#include "stm32f1xx.h" | |||||
#include "stm32f1xx_hal.h" | |||||
#include <stdbool.h> | |||||
#include <comms.h> | |||||
#include <misc.h> | |||||
#include <rs485frame.h> | |||||
#include <strobe_rng_init.h> | |||||
#include <strobe_pki.h> | |||||
#include <cycle.h> | |||||
#include <sysinit.h> | |||||
static struct pktbuf rxpktbuf; | |||||
static volatile int dorx = 0; | |||||
static uint8_t rxbuf[128]; | |||||
static volatile int gotrxdone = 0; | |||||
static volatile int gottxdone = 0; | |||||
static volatile int goterr = 0; | |||||
static volatile int rxbufsize = 0; | |||||
static volatile int rxbuftrunc = 0; | |||||
enum { | |||||
CMD_TERMINATE = 1, | |||||
CMD_WAITFOR = 2, | |||||
CMD_RUNFOR = 3, | |||||
CMD_PING = 4, | |||||
CMD_SETUNSET = 5, | |||||
CMD_ADV = 6, | |||||
CMD_CLEAR = 7, | |||||
CMD_KEY = 8, | |||||
}; | |||||
static struct comms_state cs; | |||||
static void | |||||
c13led(void) | |||||
{ | |||||
GPIO_InitTypeDef GPIO_InitStruct; | |||||
__HAL_RCC_GPIOB_CLK_ENABLE(); | |||||
__HAL_RCC_GPIOC_CLK_ENABLE(); | |||||
GPIO_InitStruct = (GPIO_InitTypeDef){ | |||||
.Pin = GPIO_PIN_13, | |||||
.Mode = GPIO_MODE_OUTPUT_PP, | |||||
.Pull = GPIO_NOPULL, | |||||
.Speed = GPIO_SPEED_FREQ_LOW, | |||||
}; | |||||
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); | |||||
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); | |||||
} | |||||
SYSINIT_VF(c13led, SI_SUB_HAL, SI_ORDER_SECOND, c13led); | |||||
void | |||||
txdone(void) | |||||
{ | |||||
gottxdone = 1; | |||||
} | |||||
void | |||||
errfunc(void) | |||||
{ | |||||
debug_printf("error\n"); | |||||
goterr = 1; | |||||
} | |||||
struct pktbuf shared_key_buf = { | |||||
.pkt = (uint8_t *)"abcd1234", | |||||
.pktlen = 8, | |||||
}; | |||||
static uint32_t rxts; | |||||
void | |||||
rxdone(const uint8_t *payload, size_t size) | |||||
{ | |||||
if (size > sizeof rxbuf) { | |||||
size = sizeof rxbuf; | |||||
rxbuftrunc = 1; | |||||
} | |||||
debug_printf("rx: %d: %02x %02x ...\n", size, payload[0], payload[1]); | |||||
memcpy(rxbuf, payload, size); | |||||
rxbufsize = size; | |||||
gotrxdone = 1; | |||||
rxts = HAL_GetTick(); | |||||
} | |||||
static inline uint32_t | |||||
letoh_32(uint8_t *v) | |||||
{ | |||||
return v[0] | (v[1] << 8) | (v[2] << 16) | ((unsigned)v[3] << 24); | |||||
} | |||||
static inline void | |||||
htole_32(uint8_t *d, uint32_t v) | |||||
{ | |||||
d[0] = v; | |||||
d[1] = v >> 8; | |||||
d[2] = v >> 16; | |||||
d[3] = v >> 24; | |||||
} | |||||
struct chaninfo { | |||||
GPIO_TypeDef *bank; | |||||
uint16_t pinnum; | |||||
bool init; | |||||
bool invert; | |||||
} chans[] = { | |||||
[0] = { .bank = GPIOB, .pinnum = GPIO_PIN_5, .invert = true, }, | |||||
[1] = { .bank = GPIOB, .pinnum = GPIO_PIN_6, .invert = true, }, | |||||
[2] = { .bank = GPIOB, .pinnum = GPIO_PIN_7, .invert = true, }, | |||||
[3] = { .bank = GPIOB, .pinnum = GPIO_PIN_9, .invert = true, }, | |||||
/* Turn on LED at start */ | |||||
[4] = { .bank = GPIOB, .pinnum = GPIO_PIN_8, .init = true, }, | |||||
}; | |||||
#define nitems(x) (sizeof(x) / sizeof *(x)) | |||||
static void | |||||
set_chan(uint32_t chan, bool val) | |||||
{ | |||||
struct chaninfo ci; | |||||
if (chan < nitems(chans)) { | |||||
ci = chans[chan]; | |||||
HAL_GPIO_WritePin(ci.bank, ci.pinnum, val ^ ci.invert ? | |||||
GPIO_PIN_SET : GPIO_PIN_RESET); | |||||
} | |||||
} | |||||
static void | |||||
setup_gpio(void) | |||||
{ | |||||
GPIO_InitTypeDef GPIO_InitStruct; | |||||
int i; | |||||
for (i = 0; i < nitems(chans); i++) { | |||||
GPIO_InitStruct = (GPIO_InitTypeDef){ | |||||
.Pin = chans[i].pinnum, | |||||
.Mode = GPIO_MODE_OUTPUT_PP, | |||||
.Pull = GPIO_NOPULL, | |||||
.Speed = GPIO_SPEED_FREQ_LOW, | |||||
}; | |||||
HAL_GPIO_Init(chans[i].bank, &GPIO_InitStruct); | |||||
set_chan(i, chans[i].init); | |||||
} | |||||
} | |||||
SYSINIT_VF(setup_gpio, SI_SUB_STANDARD, SI_ORDER_ANY, setup_gpio); | |||||
static struct sched { | |||||
uint32_t cmd; | |||||
uint32_t end_wait_tick; /* end if running, otherwise how long to wait */ | |||||
uint32_t chan; | |||||
} schedule[20]; | |||||
static int schedpos; /* position in schedule, % nitems(schedule)*/ | |||||
static int schedcnt; /* total items waiting */ | |||||
#define SCHED_ITEM(x) (schedule[(schedpos + x) % nitems(schedule)]) | |||||
#define SCHED_HEAD SCHED_ITEM(0) | |||||
#define SCHED_TAIL SCHED_ITEM(schedcnt) | |||||
static void | |||||
start_sched(struct sched *sched) | |||||
{ | |||||
sched->end_wait_tick += uwTick; | |||||
if (sched->cmd == CMD_RUNFOR) | |||||
set_chan(sched->chan, 1); | |||||
} | |||||
static bool | |||||
canproc_sched() | |||||
{ | |||||
/* nothing to do? */ | |||||
if (schedcnt == 0) | |||||
return false; | |||||
/* not yet expired */ | |||||
if (uwTick < SCHED_HEAD.end_wait_tick) | |||||
return false; | |||||
return true; | |||||
} | |||||
static void | |||||
process_sched() | |||||
{ | |||||
if (!canproc_sched()) | |||||
return; | |||||
if (SCHED_HEAD.cmd == CMD_RUNFOR) | |||||
set_chan(SCHED_HEAD.chan, 0); | |||||
/* we are done, advance */ | |||||
schedpos++; | |||||
schedcnt--; | |||||
if (schedcnt) | |||||
start_sched(&SCHED_HEAD); | |||||
} | |||||
static void | |||||
enqueue_sched(uint32_t cmd, uint32_t ticks, uint32_t chan) | |||||
{ | |||||
if (schedcnt >= nitems(schedule)) | |||||
return; | |||||
SCHED_TAIL = (struct sched){ | |||||
.cmd = cmd, | |||||
.end_wait_tick = ticks, | |||||
.chan = chan, | |||||
}; | |||||
if (schedcnt == 0) | |||||
start_sched(&SCHED_HEAD); | |||||
schedcnt++; | |||||
} | |||||
static void | |||||
procmsg(struct pktbuf inbuf, struct pktbuf *outbuf) | |||||
{ | |||||
uint32_t args[5]; | |||||
uint32_t keycnt; | |||||
int i, r, apos, cnt; | |||||
i = 1; | |||||
apos = 0; | |||||
for (i = 1, apos = 0; apos < sizeof args / sizeof *args && i + 4 <= inbuf.pktlen; i += 4, apos++) { | |||||
args[apos] = letoh_32(&inbuf.pkt[i]); | |||||
} | |||||
outbuf->pkt[0] = inbuf.pkt[0]; | |||||
outbuf->pktlen = 1; | |||||
switch (inbuf.pkt[0]) { | |||||
case CMD_WAITFOR: | |||||
if (apos == 1) | |||||
enqueue_sched(CMD_WAITFOR, args[0], -1); | |||||
break; | |||||
case CMD_RUNFOR: | |||||
if (apos == 2) | |||||
enqueue_sched(CMD_RUNFOR, args[0], args[1]); | |||||
break; | |||||
case CMD_PING: | |||||
break; | |||||
case CMD_SETUNSET: | |||||
if (apos == 2) | |||||
set_chan(args[0], args[1]); | |||||
break; | |||||
case CMD_ADV: | |||||
cnt = 1; | |||||
if (apos == 1) | |||||
cnt = args[0]; | |||||
for (i = 0; i < cnt && i < schedcnt; i++) | |||||
SCHED_ITEM(i).end_wait_tick = 0; | |||||
break; | |||||
case CMD_CLEAR: | |||||
if (schedcnt) | |||||
schedcnt = 1; | |||||
break; | |||||
case CMD_KEY: | |||||
i = 1; | |||||
keycnt = 0; | |||||
for (i = 1; i + REPORT_SIZE <= inbuf.pktlen; i += REPORT_SIZE) { | |||||
r = report_insert(&inbuf.pkt[i]); | |||||
if (!r) | |||||
break; | |||||
keycnt++; | |||||
} | |||||
htole_32(&outbuf->pkt[1], keycnt); | |||||
outbuf->pktlen = 5; | |||||
break; | |||||
default: | |||||
outbuf->pkt[0] = 0; | |||||
break; | |||||
} | |||||
} | |||||
static void | |||||
setup_button() | |||||
{ | |||||
GPIO_InitTypeDef GPIO_InitStruct; | |||||
__HAL_RCC_GPIOA_CLK_ENABLE(); | |||||
GPIO_InitStruct = (GPIO_InitTypeDef){ | |||||
.Pin = GPIO_PIN_7, | |||||
.Mode = GPIO_MODE_INPUT, | |||||
.Pull = GPIO_PULLUP, | |||||
.Speed = GPIO_SPEED_FREQ_LOW, | |||||
}; | |||||
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); | |||||
} | |||||
SYSINIT_VF(setup_button, SI_SUB_STANDARD, SI_ORDER_ANY, setup_button); | |||||
/* check if the button has been pushed, returns true once for each "push" */ | |||||
static int | |||||
button_pushed(void) | |||||
{ | |||||
static uint32_t lasthightick; | |||||
static uint32_t waspushed; | |||||
uint32_t tick; | |||||
uint8_t pinstate; | |||||
tick = HAL_GetTick(); | |||||
pinstate = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7); | |||||
if (pinstate) { | |||||
/* Reset button state */ | |||||
waspushed = 0; | |||||
lasthightick = tick; | |||||
return 0; | |||||
} | |||||
/* only return IF the it's been held for 5ms & wasn't returned before */ | |||||
if (tick - lasthightick > 5 && !waspushed) { | |||||
waspushed = 1; | |||||
return 1; | |||||
} | |||||
return 0; | |||||
} | |||||
static const uint8_t hexkeymap[] = { | |||||
[0] = 0x27, | |||||
[1] = 0x1e, | |||||
[2] = 0x1f, | |||||
[3] = 0x20, | |||||
[4] = 0x21, | |||||
[5] = 0x22, | |||||
[6] = 0x23, | |||||
[7] = 0x24, | |||||
[8] = 0x25, | |||||
[9] = 0x26, | |||||
[10] = 0x04, | |||||
[11] = 0x05, | |||||
[12] = 0x06, | |||||
[13] = 0x07, | |||||
[14] = 0x08, | |||||
[15] = 0x09, | |||||
}; | |||||
static_assert(sizeof hexkeymap == 16); | |||||
volatile uint32_t v; | |||||
void | |||||
report_blocking_insert(uint8_t rep[REPORT_SIZE]) | |||||
{ | |||||
int inserted; | |||||
do { | |||||
report_process(); | |||||
inserted = report_insert(rep); | |||||
} while(!inserted); | |||||
} | |||||
int | |||||
main() | |||||
{ | |||||
struct strobepkikey keys; | |||||
debug_printf("starting..."); | |||||
sysinit_run(); | |||||
#if 0 | |||||
/* turn on LED */ | |||||
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET); | |||||
#endif | |||||
keys = get_key(); | |||||
comms_init(&cs, procmsg, NULL, &keys.privkey, &keys.pubkey); | |||||
/* Clear out pointer to private key. */ | |||||
keys.privkey = (struct pktbuf){}; | |||||
rs485_register(txdone, rxdone, errfunc); | |||||
uint8_t txbuf[128] = "i'mhere"; | |||||
struct pktbuf txpktbuf; | |||||
txpktbuf = (struct pktbuf){ | |||||
.pkt = txbuf, | |||||
.pktlen = 7, | |||||
}; | |||||
//rs485_starttx(txpktbuf.pkt, txpktbuf.pktlen); | |||||
dorx = 1; | |||||
int laststartrx = 0; | |||||
loop: | |||||
while (canproc_sched()) | |||||
process_sched(); | |||||
#if 0 | |||||
BoardLowPowerHandler(); | |||||
#else | |||||
//(void)BoardLowPowerHandler; | |||||
#endif | |||||
/* process any pending keys */ | |||||
report_process(); | |||||
if (button_pushed()) { | |||||
/* Send the public key */ | |||||
uint8_t pubkey[EC_PUBLIC_BYTES]; | |||||
uint8_t reportbuf[REPORT_SIZE]; | |||||
const uint8_t *buf; | |||||
int remainnibbles; | |||||
uint8_t lastkey, key; | |||||
get_pubkey(pubkey); | |||||
buf = pubkey; | |||||
remainnibbles = keys.pubkey.pktlen * 2; | |||||
memset(reportbuf, 0, sizeof reportbuf); | |||||
/* clear any previous key presses */ | |||||
report_blocking_insert(reportbuf); | |||||
for (remainnibbles = keys.pubkey.pktlen * 2; remainnibbles; remainnibbles--) { | |||||
/* top nibble when even, bottom when odd */ | |||||
if (remainnibbles & 1) { | |||||
key = hexkeymap[buf[0] & 0xf]; | |||||
buf++; | |||||
} else | |||||
key = hexkeymap[buf[0] >> 4]; | |||||
lastkey = reportbuf[2]; | |||||
/* | |||||
* if previous key matches, we need to "up" it, to make | |||||
* a transition, otherwise, speed things along. | |||||
*/ | |||||
if (key == lastkey) { | |||||
reportbuf[2] = 0; | |||||
report_blocking_insert(reportbuf); | |||||
} | |||||
reportbuf[2] = key; | |||||
report_blocking_insert(reportbuf); | |||||
} | |||||
/* clear last key */ | |||||
reportbuf[2] = 0; | |||||
report_blocking_insert(reportbuf); | |||||
} | |||||
if (gottxdone) { | |||||
dorx = 1; | |||||
gottxdone = 0; | |||||
} | |||||
if (gotrxdone) { | |||||
rxpktbuf = (struct pktbuf){ | |||||
.pkt = rxbuf, | |||||
.pktlen = rxbufsize, | |||||
}; | |||||
txpktbuf = (struct pktbuf){ | |||||
.pkt = txbuf, | |||||
.pktlen = sizeof txbuf, | |||||
}; | |||||
/* process available packet */ | |||||
#if 1 | |||||
uint32_t gt; | |||||
gt = HAL_GetTick(); | |||||
reset_timer(); | |||||
start_timer(); | |||||
comms_process(&cs, rxpktbuf, &txpktbuf); | |||||
stop_timer(); | |||||
debug_printf("comproc: %u cycles, ticks: %u, fromrx: %u\n", getCycles(), HAL_GetTick() - gt, HAL_GetTick() - rxts); | |||||
#else | |||||
txpktbuf = rxpktbuf; | |||||
#endif | |||||
//debug_printf("totx: %d: %02x %02x ...\n", txpktbuf.pktlen, txpktbuf.pkt[0], txpktbuf.pkt[1]); | |||||
gotrxdone = false; | |||||
if (txpktbuf.pktlen) { | |||||
int i; | |||||
//debug_printf("rx to tx: %d\n", HAL_GetTick() - rxts); | |||||
for (i = 0; i < 1; i++) { | |||||
//HAL_Delay(20); | |||||
rs485_starttx(txpktbuf.pkt, txpktbuf.pktlen); | |||||
} | |||||
/* Make sure we don't interrupt tx */ | |||||
laststartrx = HAL_GetTick(); | |||||
} else { | |||||
dorx = 1; | |||||
} | |||||
} | |||||
if (dorx || goterr || HAL_GetTick() > laststartrx + 1000000) { | |||||
//usb_printf("dorx\r\n"); | |||||
laststartrx = HAL_GetTick(); | |||||
rs485_startrx(); | |||||
//usb_printf("startrx res: %s\r\n", rs485err); | |||||
dorx = 0; | |||||
} | |||||
goto loop; | |||||
} |
@@ -0,0 +1,87 @@ | |||||
/*- | |||||
* Copyright 2022 John-Mark Gurney. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
* SUCH DAMAGE. | |||||
* | |||||
*/ | |||||
#include <stdint.h> | |||||
#include <x25519.h> | |||||
#include <board/simpflash.h> | |||||
#include <strobe_rng_init.h> /* roundup */ | |||||
#include <strobe_pki.h> | |||||
uint8_t keyvalid; | |||||
static const uint8_t privkey[roundup(EC_PRIVATE_BYTES, sizeof(uint8_t)) / sizeof(uint8_t)] __attribute__ ((section (".savekeys"))) = { | |||||
#if 1 | |||||
0xae, 0xe7, 0xdd, 0x04, 0x84, 0xb3, 0xcd, 0x3c, | |||||
0xef, 0x25, 0x71, 0x83, 0xc4, 0x6c, 0x5d, 0x3c, | |||||
0xee, 0x98, 0xee, 0x79, 0xf2, 0x97, 0x6a, 0xe8, | |||||
0x39, 0xec, 0x7d, 0xe8, 0x23, 0xe7, 0x20, 0xdb, | |||||
#endif | |||||
}; | |||||
#include <public_key.h> | |||||
void | |||||
get_pubkey(uint8_t pubkey[EC_PUBLIC_BYTES]) | |||||
{ | |||||
x25519_base(pubkey, privkey, 1); | |||||
} | |||||
struct strobepkikey | |||||
get_key(void) | |||||
{ | |||||
struct strobepkikey spk; | |||||
uint8_t key[sizeof privkey] = {}; | |||||
uint8_t keyf[sizeof privkey] = {}; | |||||
int r; | |||||
memset(keyf, 0xff, sizeof keyf); | |||||
if (memcmp(key, privkey, sizeof privkey) == 0 || | |||||
memcmp(keyf, privkey, sizeof privkey) == 0) { | |||||
/* Generate new key */ | |||||
do { | |||||
r = strobe_randomize((uint8_t *)key, sizeof key); | |||||
if (r < 0) | |||||
continue; | |||||
} while (r != 0); | |||||
/* and write it. */ | |||||
doflash(privkey, key, sizeof key); | |||||
} | |||||
spk = (struct strobepkikey){ | |||||
.privkey.pkt = (void *)(uintptr_t)privkey, | |||||
.privkey.pktlen = EC_PRIVATE_BYTES, | |||||
.pubkey.pkt = (void *)(uintptr_t)pubkey, | |||||
.pubkey.pktlen = EC_PUBLIC_BYTES, | |||||
}; | |||||
return spk; | |||||
} |
@@ -0,0 +1,9 @@ | |||||
#include <comms.h> | |||||
struct strobepkikey { | |||||
struct pktbuf privkey; | |||||
struct pktbuf pubkey; | |||||
}; | |||||
struct strobepkikey get_key(void); | |||||
void get_pubkey(uint8_t pubkey[EC_PUBLIC_BYTES]); |
@@ -0,0 +1,62 @@ | |||||
import codecs | |||||
import struct | |||||
from usb_protocol.types import USBTransferType | |||||
from usb_protocol.emitters.descriptors import DeviceDescriptorCollection, get_string_descriptor | |||||
# Imported defines from usbd_def.h | |||||
USBD_IDX_LANGID_STR = 0x00 | |||||
USBD_IDX_MFC_STR = 0x01 | |||||
USBD_IDX_PRODUCT_STR = 0x02 | |||||
USBD_IDX_SERIAL_STR = 0x03 | |||||
USBD_IDX_CONFIG_STR = 0x04 | |||||
USBD_IDX_INTERFACE_STR = 0x05 | |||||
USB_CLASS_HID = 0x03 | |||||
USB_CLASS_HID_NO_BOOT = 0x00 | |||||
USB_CLASS_HID_BOOT = 0x01 | |||||
USB_PROTOCOL_HID_NONE = 0x00 | |||||
USB_PROTOCOL_HID_KEYBOARD = 0x01 | |||||
USB_PROTOCOL_HID_MOUSE = 0x02 | |||||
collection = DeviceDescriptorCollection() | |||||
with collection.DeviceDescriptor() as d: | |||||
# https://pid.codes/1209/ | |||||
d.idVendor = 0x1209 | |||||
d.idProduct = 0x001 | |||||
d.bNumConfigurations = 1 | |||||
# Hack for ST reference code | |||||
d.iProduct = USBD_IDX_PRODUCT_STR | |||||
collection.add_descriptor(get_string_descriptor('RS-485 to HID'), d.iProduct) | |||||
with collection.ConfigurationDescriptor() as c: | |||||
with c.InterfaceDescriptor() as i: | |||||
i.bInterfaceNumber = 0 | |||||
i.bInterfaceClass = USB_CLASS_HID | |||||
i.bInterfaceSubclass = USB_CLASS_HID_BOOT | |||||
i.bInterfaceProtocol = USB_PROTOCOL_HID_KEYBOARD | |||||
report_desc = codecs.decode('05010906a101050719e029e71500250175019508810295017508810195057501050819012905910295017503910195067508150025650507190029658100c0', 'hex') | |||||
collection.add_descriptor(report_desc, descriptor_type=0x22) | |||||
hid_desc = codecs.decode('09211101000122', 'hex') + struct.pack('<H', len(report_desc)) | |||||
# add HID Descriptor after interface, but before | |||||
# endpoint descriptors per HID v1.11 § 7.1 | |||||
i.add_subordinate_descriptor(hid_desc) | |||||
# No OUT endpoint. Use the SET_REPORT via the control EP | |||||
with i.EndpointDescriptor() as e: | |||||
e.bEndpointAddress = 0x81 | |||||
e.bmAttributes = USBTransferType.INTERRUPT | |||||
# it'd be good to adjust based upon FS vs LS | |||||
e.bInterval = 10 # polling interval (LS/FS in ms) | |||||
# Full-speed max size | |||||
e.wMaxPacketSize = 64 |
@@ -0,0 +1,328 @@ | |||||
/*- | |||||
* Copyright 2022 John-Mark Gurney. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* 3. If you are STMicroelectronics N.V., one of it's subsidiaries, a | |||||
* subsidiary of an owner of STMicroelectronics N.V., or an employee, | |||||
* contractor, or agent of any of the preceeding entities, you are not | |||||
* allowed to use this code, in either source or binary forms. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
* SUCH DAMAGE. | |||||
* | |||||
*/ | |||||
#include "usb_hid_def.h" | |||||
#include <misc.h> /* debug_printf */ | |||||
#include <stdbool.h> | |||||
#include <usb_hid_base.h> | |||||
static uint8_t Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx); | |||||
static uint8_t DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx); | |||||
static uint8_t Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); | |||||
static uint8_t DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum); | |||||
#if 0 | |||||
static uint8_t DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum); | |||||
#endif | |||||
static uint8_t ep0rxready(USBD_HandleTypeDef *pdev); | |||||
#define USB_HID_EPIN_ADDR 0x81 | |||||
#define REPORT_CNT 10 | |||||
enum hid_state { | |||||
IDLE, | |||||
BUSY, | |||||
}; | |||||
struct usb_hid_softc { | |||||
enum hid_state state; | |||||
uint8_t led_status[2]; | |||||
uint8_t idle_time; | |||||
uint8_t report_protocol; | |||||
}; | |||||
static USBD_HandleTypeDef *hid_handle; | |||||
static uint32_t next_idle_report; | |||||
static uint8_t reports[REPORT_CNT][REPORT_SIZE] = { | |||||
/* mod pad keys... */ | |||||
#if 0 | |||||
[0] = { 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00 }, | |||||
[1] = { 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 }, | |||||
[2] = { 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00 }, | |||||
[3] = { 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00 }, | |||||
[4] = { 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00 }, | |||||
[5] = { 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00 }, | |||||
[6] = { 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 }, | |||||
[7] = { 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 }, | |||||
[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, | |||||
[9] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, | |||||
#endif | |||||
}; | |||||
/* | |||||
* Ring buffer for reports. | |||||
* | |||||
* reporthead: next available report | |||||
* reporttail: next empty slot | |||||
* | |||||
* reporthead == reporttail: ring buffer is empty | |||||
* reporttail == reporthead - 1: ring buffer is full | |||||
*/ | |||||
static uint8_t reporthead = 0; | |||||
static uint8_t reporttail = 0; | |||||
static uint32_t reportoverflow; | |||||
/* as -1 % posint == -1, adding REPORT_CNT keeps it positive if x is val - 1 when val is 0 */ | |||||
#define MAKE_POS(x) (((x) + REPORT_CNT) % REPORT_CNT) | |||||
static bool | |||||
report_is_avail(void) | |||||
{ | |||||
return reporthead != reporttail; | |||||
} | |||||
static uint8_t * | |||||
report_next(void) | |||||
{ | |||||
int oldrep; | |||||
/* no new report, don't advance, return previous */ | |||||
if (!report_is_avail()) | |||||
return reports[MAKE_POS(reporthead - 1)]; | |||||
oldrep = reporthead; | |||||
reporthead = MAKE_POS(reporthead + 1); | |||||
return reports[oldrep]; | |||||
} | |||||
void | |||||
report_process(void) | |||||
{ | |||||
struct usb_hid_softc *hid; | |||||
uint32_t tick; | |||||
if (hid_handle == NULL) | |||||
return; | |||||
hid = (struct usb_hid_softc *)hid_handle->pClassData; | |||||
if (hid_handle->dev_state != USBD_STATE_CONFIGURED || | |||||
hid->state != IDLE) { | |||||
return; | |||||
} | |||||
tick = HAL_GetTick(); | |||||
if (!report_is_avail() && tick < next_idle_report) | |||||
return; | |||||
next_idle_report = tick + hid->idle_time; | |||||
hid->state = BUSY; | |||||
USBD_LL_Transmit(hid_handle, USB_HID_EPIN_ADDR, report_next(), REPORT_SIZE); | |||||
} | |||||
/* | |||||
* This could be smarter by collapsing reports. The algorithm to do | |||||
* so is a bit tricky, and likely not needed, BUT it does mean for a | |||||
* simple algorithm, you could be limited to 50 chars per second assuming | |||||
* 10ms report timing (one down, one up). | |||||
* | |||||
* Return true if successful, false if overflowed. | |||||
*/ | |||||
int | |||||
report_insert(uint8_t rep[REPORT_SIZE]) | |||||
{ | |||||
uint8_t newtail; | |||||
newtail = MAKE_POS(reporttail + 1); | |||||
if (newtail == reporthead) { | |||||
reportoverflow++; | |||||
return 0; | |||||
} | |||||
memcpy(reports[reporttail], rep, sizeof reports[reporttail]); | |||||
reporttail = newtail; | |||||
return 1; | |||||
} | |||||
enum { | |||||
HID_REQ_GET_REPORT = 0x01, | |||||
HID_REQ_GET_IDLE = 0x02, | |||||
HID_REQ_GET_PROTOCOL = 0x03, | |||||
HID_REQ_SET_REPORT = 0x09, | |||||
HID_REQ_SET_IDLE = 0x0a, | |||||
HID_REQ_SET_PROTOCOL = 0x0b, | |||||
}; | |||||
USBD_ClassTypeDef usb_hid_def = { | |||||
.Init = Init, | |||||
.DeInit = DeInit, | |||||
.Setup = Setup, | |||||
.EP0_RxReady = ep0rxready, | |||||
.DataIn = DataIn, | |||||
#if 0 | |||||
.DataOut = DataOut, | |||||
#endif | |||||
/* Get*Desc not used */ | |||||
}; | |||||
static uint8_t | |||||
Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx) | |||||
{ | |||||
struct usb_hid_softc *hid; | |||||
int ret; | |||||
ret = 0; | |||||
usb_hid_epopen(pdev); | |||||
/* allocate state data */ | |||||
hid = (void *)USBD_malloc(sizeof(struct usb_hid_softc)); | |||||
if (hid == NULL) { | |||||
ret = 1; | |||||
} else { | |||||
*hid = (struct usb_hid_softc){ | |||||
.idle_time = 10, | |||||
}; | |||||
pdev->pClassData = hid; | |||||
hid_handle = pdev; | |||||
} | |||||
return ret; | |||||
} | |||||
static uint8_t | |||||
DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx) | |||||
{ | |||||
usb_hid_epclose(pdev); | |||||
if (pdev->pClassData != NULL) { | |||||
USBD_free(pdev->pClassData); | |||||
pdev->pClassData = NULL; | |||||
hid_handle = NULL; | |||||
} | |||||
return 0; | |||||
} | |||||
#define MAKE_CASE_REQ(type, req) ((((type) << 8) & USB_REQ_TYPE_MASK) | (req)) | |||||
#define USB_CASE_REQ(req) MAKE_CASE_REQ((req)->bmRequest, (req)->bRequest) | |||||
static void | |||||
send_ctlresp(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req, const uint8_t *buf, size_t len) | |||||
{ | |||||
len = MIN(req->wLength, len); | |||||
USBD_CtlSendData(pdev, (uint8_t *)(uintptr_t)buf, len); | |||||
} | |||||
/* | |||||
* Handle non-standard control SETUP requests. | |||||
* | |||||
* This includes requests sent to the interface and other special | |||||
* requests. | |||||
*/ | |||||
static uint8_t | |||||
Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) | |||||
{ | |||||
struct usb_hid_softc *hid; | |||||
uint8_t ret; | |||||
ret = USBD_OK; | |||||
hid = (struct usb_hid_softc *)pdev->pClassData; | |||||
switch (USB_CASE_REQ(req)) { | |||||
case MAKE_CASE_REQ(USB_REQ_TYPE_STANDARD, USB_REQ_GET_DESCRIPTOR): | |||||
USBD_GetDescriptor(pdev, req); | |||||
break; | |||||
case MAKE_CASE_REQ(USB_REQ_TYPE_CLASS, HID_REQ_GET_REPORT): | |||||
send_ctlresp(pdev, req, report_next(), REPORT_SIZE); | |||||
break; | |||||
case MAKE_CASE_REQ(USB_REQ_TYPE_CLASS, HID_REQ_SET_REPORT): | |||||
break; | |||||
case MAKE_CASE_REQ(USB_REQ_TYPE_CLASS, HID_REQ_GET_IDLE): | |||||
send_ctlresp(pdev, req, &hid->idle_time, sizeof hid->idle_time); | |||||
break; | |||||
case MAKE_CASE_REQ(USB_REQ_TYPE_CLASS, HID_REQ_SET_IDLE): | |||||
hid->idle_time = (uint8_t)(req->wValue >> 8); | |||||
break; | |||||
case MAKE_CASE_REQ(USB_REQ_TYPE_CLASS, HID_REQ_GET_PROTOCOL): | |||||
send_ctlresp(pdev, req, &hid->report_protocol, sizeof hid->idle_time); | |||||
break; | |||||
case MAKE_CASE_REQ(USB_REQ_TYPE_CLASS, HID_REQ_SET_PROTOCOL): | |||||
hid->report_protocol = !!req->wValue; | |||||
break; | |||||
default: | |||||
USBD_CtlError(pdev, req); | |||||
ret = USBD_FAIL; | |||||
break; | |||||
} | |||||
return ret; | |||||
} | |||||
static uint8_t | |||||
DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum) | |||||
{ | |||||
struct usb_hid_softc *hid; | |||||
hid = (struct usb_hid_softc *)pdev->pClassData; | |||||
/* Previously queued data was sent */ | |||||
hid->state = IDLE; | |||||
return USBD_OK; | |||||
} | |||||
#if 0 | |||||
static uint8_t | |||||
DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum) | |||||
{ | |||||
/* received led status */ | |||||
return USBD_OK; | |||||
} | |||||
#endif | |||||
static uint8_t | |||||
ep0rxready(USBD_HandleTypeDef *pdev) | |||||
{ | |||||
return USBD_OK; | |||||
} |
@@ -0,0 +1,6 @@ | |||||
#include <stdint.h> | |||||
#define REPORT_SIZE 8 | |||||
void report_process(void); | |||||
int report_insert(uint8_t rep[REPORT_SIZE]); |
@@ -1,16 +1,18 @@ | |||||
/* | /* | ||||
* Code for initalizing USB CDC via the SYSINIT mechanism. | |||||
* Code for reseting USB via the SYSINIT mechanism. | |||||
* | |||||
* Note: This works for both Node151 and BluePill (F1) as PA12 is | |||||
* USB_DP on both. | |||||
*/ | */ | ||||
#include <sysinit.h> | #include <sysinit.h> | ||||
#include <stm32f1xx_hal_gpio.h> | #include <stm32f1xx_hal_gpio.h> | ||||
#include <stm32f1xx_hal_pcd.h> | |||||
#include <usb_device.h> | #include <usb_device.h> | ||||
static void | static void | ||||
usb_cdc_init(const void *foo) | |||||
usb_reset(void) | |||||
{ | { | ||||
/* | /* | ||||
* pretend that the device was newly plugged in | * pretend that the device was newly plugged in | ||||
@@ -32,18 +34,5 @@ usb_cdc_init(const void *foo) | |||||
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET); | HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET); | ||||
HAL_Delay(10); | HAL_Delay(10); | ||||
MX_USB_DEVICE_Init(); | |||||
} | |||||
extern PCD_HandleTypeDef hpcd_USB_FS; | |||||
void | |||||
USB_LP_IRQHandler(void) | |||||
{ | |||||
HAL_PCD_IRQHandler(&hpcd_USB_FS); | |||||
} | } | ||||
SYSINIT(usb_cdc_init, SI_SUB_CONSOLE, SI_ORDER_FIRST, usb_cdc_init, NULL); | |||||
SYSINIT_VF(usb_reset, SI_SUB_USB, SI_ORDER_FIRST, usb_reset); |
@@ -55,6 +55,8 @@ _estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */ | |||||
_Min_Heap_Size = 0x200 ; /* required amount of heap */ | _Min_Heap_Size = 0x200 ; /* required amount of heap */ | ||||
_Min_Stack_Size = 0x400 ; /* required amount of stack */ | _Min_Stack_Size = 0x400 ; /* required amount of stack */ | ||||
_flash_page_size = 0x400 ; /* size of a flash page */ | |||||
/* Memories definition */ | /* Memories definition */ | ||||
MEMORY | MEMORY | ||||
{ | { | ||||
@@ -100,7 +102,6 @@ SECTIONS | |||||
__start_set_sysinit_set = .; | __start_set_sysinit_set = .; | ||||
KEEP(*(set_sysinit_set)) /* sysinit linker sets */ | KEEP(*(set_sysinit_set)) /* sysinit linker sets */ | ||||
__stop_set_sysinit_set = .; | __stop_set_sysinit_set = .; | ||||
. = ALIGN(4); | |||||
} >FLASH | } >FLASH | ||||
.ARM.extab : { | .ARM.extab : { | ||||
@@ -162,6 +163,20 @@ SECTIONS | |||||
} >RAM AT> FLASH | } >RAM AT> FLASH | ||||
.savestate : | |||||
{ | |||||
. = ALIGN(_flash_page_size); | |||||
*(.savestate) /* save state */ | |||||
. = ALIGN(_flash_page_size); | |||||
} >FLASH | |||||
.savekeys : | |||||
{ | |||||
. = ALIGN(_flash_page_size); | |||||
*(.savekeys) /* save keys */ | |||||
. = ALIGN(_flash_page_size); | |||||
} >FLASH | |||||
/* Uninitialized data section into "RAM" Ram type memory */ | /* Uninitialized data section into "RAM" Ram type memory */ | ||||
. = ALIGN(4); | . = ALIGN(4); | ||||
.bss : | .bss : | ||||
@@ -0,0 +1,216 @@ | |||||
import codecs | |||||
import construct | |||||
import importlib | |||||
import sys | |||||
from usb_protocol.types.descriptors.standard import ConfigurationDescriptor, DescriptorFormat, EndpointDescriptor, InterfaceDescriptor, StandardDescriptorNumbers | |||||
from string import Template | |||||
# Note this file is under the following copyright: | |||||
copyright = '''/*- | |||||
* Copyright 2022 John-Mark Gurney. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* 3. If you are STMicroelectronics N.V., one of it's subsidiaries, a | |||||
* subsidiary of an owner of STMicroelectronics N.V., or an employee, | |||||
* contractor, or agent of any of the preceeding entities, you are not | |||||
* allowed to use this code, in either source or binary forms. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
* SUCH DAMAGE. | |||||
* | |||||
*/ | |||||
''' | |||||
UniDesc = DescriptorFormat( | |||||
"bLength" / construct.Int8ul, | |||||
"bDescriptorType" / construct.Int8ul, | |||||
) | |||||
descparsers = { | |||||
StandardDescriptorNumbers.INTERFACE: InterfaceDescriptor.parse, | |||||
StandardDescriptorNumbers.ENDPOINT: EndpointDescriptor.parse, | |||||
} | |||||
def getepdescs(confdesc): | |||||
ret = [] | |||||
lastdesc = ConfigurationDescriptor.parse(confdesc) | |||||
pos = lastdesc.bLength | |||||
while pos < len(confdesc): | |||||
descparser = descparsers.get(confdesc[pos + 1], UniDesc.parse) | |||||
desc = descparser(confdesc[pos:]) | |||||
if confdesc[pos + 1] == StandardDescriptorNumbers.ENDPOINT: | |||||
ret.append(desc) | |||||
lastdesc = desc | |||||
pos += lastdesc.bLength | |||||
return ret | |||||
if __name__ == '__main__': | |||||
usbname = sys.argv[1] | |||||
mod = importlib.import_module(usbname) | |||||
collection = mod.collection | |||||
sorteddesc = sorted(collection) | |||||
descs = { (x, y): z for x, y, z in collection } | |||||
for value, index, raw in sorteddesc: | |||||
print(int(value), repr(index), codecs.encode(raw, 'hex')) | |||||
p = lambda *args, **kwargs: print(*args, **kwargs, file=fp) | |||||
with open('%s_base.h' % usbname, 'w') as fp: | |||||
p(Template('''${copyright} | |||||
#include <usbd_core.h> | |||||
extern USBD_ClassTypeDef ${usbname}_def; | |||||
void USBD_GetDescriptor(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); | |||||
void ${usbname}_init(void); | |||||
void ${usbname}_epopen(USBD_HandleTypeDef *pdev); | |||||
void ${usbname}_epclose(USBD_HandleTypeDef *pdev); | |||||
''').substitute(locals())) | |||||
with open('%s_base.c' % usbname, 'w') as fp: | |||||
descstrings = '\n'.join('const uint8_t desc_%d_%d[] = { %s };' % (x, y, ', '.join(hex(w) for w in z)) for x, y, z in sorteddesc) | |||||
descstructs = '\n'.join('\t{ .type = %d, .subnumb = %d, .datalen = %d, .data = desc_%d_%d },' % (x, y, len(z), x, y) for x, y, z in sorteddesc) | |||||
epdescs = getepdescs(descs[(2, 0)]) | |||||
print(repr(epdescs)) | |||||
epopencode = '\n\t'.join(sum(([ | |||||
'USBD_LL_OpenEP(pdev, %#x, %d, %d);' % (ep.bEndpointAddress, ep.bmAttributes & 0x3, ep.wMaxPacketSize), | |||||
'pdev->%s[%#x & 0xFU].is_used = 1;' % ('ep_in' if ep.bEndpointAddress & 0x80 else 'ep_out', ep.bEndpointAddress) | |||||
] for ep in epdescs), [])) | |||||
epclosecode = '\n\t'.join(sum(([ | |||||
'USBD_LL_CloseEP(pdev, %#x);' % (ep.bEndpointAddress), | |||||
'pdev->%s[%#x & 0xFU].is_used = 0;' % ('ep_in' if ep.bEndpointAddress & 0x80 else 'ep_out', ep.bEndpointAddress) | |||||
] for ep in epdescs), [])) | |||||
print(repr(epopencode)) | |||||
p(Template('''${copyright} | |||||
#include "${usbname}_base.h" | |||||
#include <sysinit.h> | |||||
#include <usbd_core.h> | |||||
/* #include <usbd_desc.h> */ | |||||
/* #include <usbd_conf.h> */ | |||||
static USBD_HandleTypeDef usbd_inst; | |||||
void USBD_GetDescriptor(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); | |||||
${descstrings} | |||||
static const struct { | |||||
uint8_t type; | |||||
uint8_t subnumb; | |||||
uint8_t datalen; | |||||
const uint8_t *data; | |||||
} descriptors[] = { | |||||
${descstructs} | |||||
}; | |||||
void | |||||
USBD_GetDescriptor(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) | |||||
{ | |||||
uint8_t const *buf; | |||||
uint8_t buflen; | |||||
uint8_t type, subnumb; | |||||
int i; | |||||
type = req->wValue >> 8; | |||||
subnumb = req->wValue & 0xff; | |||||
for (i = 0; i < sizeof descriptors / sizeof *descriptors; i++) { | |||||
if (descriptors[i].type == type && descriptors[i].subnumb == subnumb) { | |||||
buf = descriptors[i].data; | |||||
buflen = descriptors[i].datalen; | |||||
if ((buflen != 0U) && (req->wLength != 0U)) { | |||||
buflen = MIN(buflen, req->wLength); | |||||
USBD_CtlSendData(pdev, (uint8_t *)(uintptr_t)buf, buflen); | |||||
} | |||||
if(req->wLength == 0U) { | |||||
USBD_CtlSendStatus(pdev); | |||||
} | |||||
return; | |||||
} | |||||
} | |||||
USBD_CtlError(pdev , req); | |||||
} | |||||
void | |||||
${usbname}_init(void) | |||||
{ | |||||
if (USBD_Init(&usbd_inst, NULL/*Desc, not used*/, DEVICE_FS) != USBD_OK) | |||||
Error_Handler(); | |||||
if (USBD_RegisterClass(&usbd_inst, &${usbname}_def) != USBD_OK) | |||||
Error_Handler(); | |||||
if (USBD_Start(&usbd_inst) != USBD_OK) | |||||
Error_Handler(); | |||||
} | |||||
SYSINIT_VF(${usbname}_init, SI_SUB_USB, SI_ORDER_MIDDLE, ${usbname}_init); | |||||
void | |||||
${usbname}_epopen(USBD_HandleTypeDef *pdev) | |||||
{ | |||||
${epopencode} | |||||
} | |||||
void | |||||
${usbname}_epclose(USBD_HandleTypeDef *pdev) | |||||
{ | |||||
${epclosecode} | |||||
} | |||||
extern PCD_HandleTypeDef hpcd_USB_FS; | |||||
void | |||||
USB_LP_IRQHandler(void) | |||||
{ | |||||
HAL_PCD_IRQHandler(&hpcd_USB_FS); | |||||
} | |||||
static volatile uint32_t holding; | |||||
void | |||||
Error_Handler(void) | |||||
{ | |||||
#if 0 | |||||
debug_printf("error_handler\\n"); | |||||
#endif | |||||
for (;;) holding++; | |||||
}''').substitute(locals())) | |||||
print('enda') |
@@ -0,0 +1,853 @@ | |||||
/** | |||||
****************************************************************************** | |||||
* @file usbd_req.c | |||||
* @author MCD Application Team | |||||
* @brief This file provides the standard USB requests following chapter 9. | |||||
****************************************************************************** | |||||
* @attention | |||||
* | |||||
* <h2><center>© Copyright (c) 2015 STMicroelectronics. | |||||
* All rights reserved.</center></h2> | |||||
* | |||||
* This software component is licensed by ST under Ultimate Liberty license | |||||
* SLA0044, the "License"; You may not use this file except in compliance with | |||||
* the License. You may obtain a copy of the License at: | |||||
* http://www.st.com/SLA0044 | |||||
* | |||||
****************************************************************************** | |||||
*/ | |||||
/* Includes ------------------------------------------------------------------*/ | |||||
#include "usbd_ctlreq.h" | |||||
#include "usbd_ioreq.h" | |||||
/** @addtogroup STM32_USBD_STATE_DEVICE_LIBRARY | |||||
* @{ | |||||
*/ | |||||
/** @defgroup USBD_REQ | |||||
* @brief USB standard requests module | |||||
* @{ | |||||
*/ | |||||
/** @defgroup USBD_REQ_Private_TypesDefinitions | |||||
* @{ | |||||
*/ | |||||
/** | |||||
* @} | |||||
*/ | |||||
/** @defgroup USBD_REQ_Private_Defines | |||||
* @{ | |||||
*/ | |||||
/** | |||||
* @} | |||||
*/ | |||||
/** @defgroup USBD_REQ_Private_Macros | |||||
* @{ | |||||
*/ | |||||
/** | |||||
* @} | |||||
*/ | |||||
/** @defgroup USBD_REQ_Private_Variables | |||||
* @{ | |||||
*/ | |||||
/** | |||||
* @} | |||||
*/ | |||||
/** @defgroup USBD_REQ_Private_FunctionPrototypes | |||||
* @{ | |||||
*/ | |||||
#if 0 | |||||
static | |||||
#endif | |||||
void USBD_GetDescriptor(USBD_HandleTypeDef *pdev , | |||||
USBD_SetupReqTypedef *req); | |||||
static void USBD_SetAddress(USBD_HandleTypeDef *pdev , | |||||
USBD_SetupReqTypedef *req); | |||||
static void USBD_SetConfig(USBD_HandleTypeDef *pdev , | |||||
USBD_SetupReqTypedef *req); | |||||
static void USBD_GetConfig(USBD_HandleTypeDef *pdev , | |||||
USBD_SetupReqTypedef *req); | |||||
static void USBD_GetStatus(USBD_HandleTypeDef *pdev , | |||||
USBD_SetupReqTypedef *req); | |||||
static void USBD_SetFeature(USBD_HandleTypeDef *pdev , | |||||
USBD_SetupReqTypedef *req); | |||||
static void USBD_ClrFeature(USBD_HandleTypeDef *pdev , | |||||
USBD_SetupReqTypedef *req); | |||||
static uint8_t USBD_GetLen(uint8_t *buf); | |||||
/** | |||||
* @} | |||||
*/ | |||||
/** @defgroup USBD_REQ_Private_Functions | |||||
* @{ | |||||
*/ | |||||
/** | |||||
* @brief USBD_StdDevReq | |||||
* Handle standard usb device requests | |||||
* @param pdev: device instance | |||||
* @param req: usb request | |||||
* @retval status | |||||
*/ | |||||
USBD_StatusTypeDef USBD_StdDevReq (USBD_HandleTypeDef *pdev , USBD_SetupReqTypedef *req) | |||||
{ | |||||
USBD_StatusTypeDef ret = USBD_OK; | |||||
switch (req->bmRequest & USB_REQ_TYPE_MASK) | |||||
{ | |||||
case USB_REQ_TYPE_CLASS: | |||||
case USB_REQ_TYPE_VENDOR: | |||||
pdev->pClass->Setup(pdev, req); | |||||
break; | |||||
case USB_REQ_TYPE_STANDARD: | |||||
switch (req->bRequest) | |||||
{ | |||||
case USB_REQ_GET_DESCRIPTOR: | |||||
USBD_GetDescriptor (pdev, req); | |||||
break; | |||||
case USB_REQ_SET_ADDRESS: | |||||
USBD_SetAddress (pdev, req); | |||||
break; | |||||
case USB_REQ_SET_CONFIGURATION: | |||||
USBD_SetConfig (pdev, req); | |||||
break; | |||||
case USB_REQ_GET_CONFIGURATION: | |||||
USBD_GetConfig (pdev, req); | |||||
break; | |||||
case USB_REQ_GET_STATUS: | |||||
USBD_GetStatus (pdev, req); | |||||
break; | |||||
case USB_REQ_SET_FEATURE: | |||||
USBD_SetFeature (pdev, req); | |||||
break; | |||||
case USB_REQ_CLEAR_FEATURE: | |||||
USBD_ClrFeature (pdev, req); | |||||
break; | |||||
default: | |||||
USBD_CtlError(pdev, req); | |||||
break; | |||||
} | |||||
break; | |||||
default: | |||||
USBD_CtlError(pdev, req); | |||||
break; | |||||
} | |||||
return ret; | |||||
} | |||||
/** | |||||
* @brief USBD_StdItfReq | |||||
* Handle standard usb interface requests | |||||
* @param pdev: device instance | |||||
* @param req: usb request | |||||
* @retval status | |||||
*/ | |||||
USBD_StatusTypeDef USBD_StdItfReq (USBD_HandleTypeDef *pdev , USBD_SetupReqTypedef *req) | |||||
{ | |||||
USBD_StatusTypeDef ret = USBD_OK; | |||||
switch (req->bmRequest & USB_REQ_TYPE_MASK) | |||||
{ | |||||
case USB_REQ_TYPE_CLASS: | |||||
case USB_REQ_TYPE_VENDOR: | |||||
case USB_REQ_TYPE_STANDARD: | |||||
switch (pdev->dev_state) | |||||
{ | |||||
case USBD_STATE_DEFAULT: | |||||
case USBD_STATE_ADDRESSED: | |||||
case USBD_STATE_CONFIGURED: | |||||
if (LOBYTE(req->wIndex) <= USBD_MAX_NUM_INTERFACES) | |||||
{ | |||||
ret = (USBD_StatusTypeDef)pdev->pClass->Setup (pdev, req); | |||||
if ((req->wLength == 0U) && (ret == USBD_OK)) | |||||
{ | |||||
USBD_CtlSendStatus(pdev); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
USBD_CtlError(pdev, req); | |||||
} | |||||
break; | |||||
default: | |||||
USBD_CtlError(pdev, req); | |||||
break; | |||||
} | |||||
break; | |||||
default: | |||||
USBD_CtlError(pdev, req); | |||||
break; | |||||
} | |||||
return USBD_OK; | |||||
} | |||||
/** | |||||
* @brief USBD_StdEPReq | |||||
* Handle standard usb endpoint requests | |||||
* @param pdev: device instance | |||||
* @param req: usb request | |||||
* @retval status | |||||
*/ | |||||
USBD_StatusTypeDef USBD_StdEPReq (USBD_HandleTypeDef *pdev , USBD_SetupReqTypedef *req) | |||||
{ | |||||
uint8_t ep_addr; | |||||
USBD_StatusTypeDef ret = USBD_OK; | |||||
USBD_EndpointTypeDef *pep; | |||||
ep_addr = LOBYTE(req->wIndex); | |||||
switch (req->bmRequest & USB_REQ_TYPE_MASK) | |||||
{ | |||||
case USB_REQ_TYPE_CLASS: | |||||
case USB_REQ_TYPE_VENDOR: | |||||
pdev->pClass->Setup (pdev, req); | |||||
break; | |||||
case USB_REQ_TYPE_STANDARD: | |||||
/* Check if it is a class request */ | |||||
if ((req->bmRequest & 0x60U) == 0x20U) | |||||
{ | |||||
ret = (USBD_StatusTypeDef)pdev->pClass->Setup (pdev, req); | |||||
return ret; | |||||
} | |||||
switch (req->bRequest) | |||||
{ | |||||
case USB_REQ_SET_FEATURE : | |||||
switch (pdev->dev_state) | |||||
{ | |||||
case USBD_STATE_ADDRESSED: | |||||
if ((ep_addr != 0x00U) && (ep_addr != 0x80U)) | |||||
{ | |||||
USBD_LL_StallEP(pdev, ep_addr); | |||||
USBD_LL_StallEP(pdev, 0x80U); | |||||
} | |||||
else | |||||
{ | |||||
USBD_CtlError(pdev, req); | |||||
} | |||||
break; | |||||
case USBD_STATE_CONFIGURED: | |||||
if (req->wValue == USB_FEATURE_EP_HALT) | |||||
{ | |||||
if ((ep_addr != 0x00U) && (ep_addr != 0x80U) && (req->wLength == 0x00U)) | |||||
{ | |||||
USBD_LL_StallEP(pdev, ep_addr); | |||||
} | |||||
} | |||||
USBD_CtlSendStatus(pdev); | |||||
break; | |||||
default: | |||||
USBD_CtlError(pdev, req); | |||||
break; | |||||
} | |||||
break; | |||||
case USB_REQ_CLEAR_FEATURE : | |||||
switch (pdev->dev_state) | |||||
{ | |||||
case USBD_STATE_ADDRESSED: | |||||
if ((ep_addr != 0x00U) && (ep_addr != 0x80U)) | |||||
{ | |||||
USBD_LL_StallEP(pdev, ep_addr); | |||||
USBD_LL_StallEP(pdev, 0x80U); | |||||
} | |||||
else | |||||
{ | |||||
USBD_CtlError(pdev, req); | |||||
} | |||||
break; | |||||
case USBD_STATE_CONFIGURED: | |||||
if (req->wValue == USB_FEATURE_EP_HALT) | |||||
{ | |||||
if ((ep_addr & 0x7FU) != 0x00U) | |||||
{ | |||||
USBD_LL_ClearStallEP(pdev, ep_addr); | |||||
} | |||||
USBD_CtlSendStatus(pdev); | |||||
} | |||||
break; | |||||
default: | |||||
USBD_CtlError(pdev, req); | |||||
break; | |||||
} | |||||
break; | |||||
case USB_REQ_GET_STATUS: | |||||
switch (pdev->dev_state) | |||||
{ | |||||
case USBD_STATE_ADDRESSED: | |||||
if ((ep_addr != 0x00U) && (ep_addr != 0x80U)) | |||||
{ | |||||
USBD_CtlError(pdev, req); | |||||
break; | |||||
} | |||||
pep = ((ep_addr & 0x80U) == 0x80U) ? &pdev->ep_in[ep_addr & 0x7FU]:\ | |||||
&pdev->ep_out[ep_addr & 0x7FU]; | |||||
pep->status = 0x0000U; | |||||
USBD_CtlSendData (pdev, (uint8_t *)(void *)&pep->status, 2U); | |||||
break; | |||||
case USBD_STATE_CONFIGURED: | |||||
if((ep_addr & 0x80U) == 0x80U) | |||||
{ | |||||
if (pdev->ep_in[ep_addr & 0xFU].is_used == 0U) | |||||
{ | |||||
USBD_CtlError(pdev, req); | |||||
break; | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
if (pdev->ep_out[ep_addr & 0xFU].is_used == 0U) | |||||
{ | |||||
USBD_CtlError(pdev, req); | |||||
break; | |||||
} | |||||
} | |||||
pep = ((ep_addr & 0x80U) == 0x80U) ? &pdev->ep_in[ep_addr & 0x7FU]:\ | |||||
&pdev->ep_out[ep_addr & 0x7FU]; | |||||
if ((ep_addr == 0x00U) || (ep_addr == 0x80U)) | |||||
{ | |||||
pep->status = 0x0000U; | |||||
} | |||||
else if(USBD_LL_IsStallEP(pdev, ep_addr)) | |||||
{ | |||||
pep->status = 0x0001U; | |||||
} | |||||
else | |||||
{ | |||||
pep->status = 0x0000U; | |||||
} | |||||
USBD_CtlSendData (pdev, (uint8_t *)(void *)&pep->status, 2U); | |||||
break; | |||||
default: | |||||
USBD_CtlError(pdev, req); | |||||
break; | |||||
} | |||||
break; | |||||
default: | |||||
USBD_CtlError(pdev, req); | |||||
break; | |||||
} | |||||
break; | |||||
default: | |||||
USBD_CtlError(pdev, req); | |||||
break; | |||||
} | |||||
return ret; | |||||
} | |||||
#if 0 | |||||
/** | |||||
* @brief USBD_GetDescriptor | |||||
* Handle Get Descriptor requests | |||||
* @param pdev: device instance | |||||
* @param req: usb request | |||||
* @retval status | |||||
*/ | |||||
static void USBD_GetDescriptor(USBD_HandleTypeDef *pdev , | |||||
USBD_SetupReqTypedef *req) | |||||
{ | |||||
uint16_t len; | |||||
uint8_t *pbuf; | |||||
switch (req->wValue >> 8) | |||||
{ | |||||
#if (USBD_LPM_ENABLED == 1U) | |||||
case USB_DESC_TYPE_BOS: | |||||
pbuf = pdev->pDesc->GetBOSDescriptor(pdev->dev_speed, &len); | |||||
break; | |||||
#endif | |||||
case USB_DESC_TYPE_DEVICE: | |||||
pbuf = pdev->pDesc->GetDeviceDescriptor(pdev->dev_speed, &len); | |||||
break; | |||||
case USB_DESC_TYPE_CONFIGURATION: | |||||
if(pdev->dev_speed == USBD_SPEED_HIGH ) | |||||
{ | |||||
pbuf = (uint8_t *)pdev->pClass->GetHSConfigDescriptor(&len); | |||||
pbuf[1] = USB_DESC_TYPE_CONFIGURATION; | |||||
} | |||||
else | |||||
{ | |||||
pbuf = (uint8_t *)pdev->pClass->GetFSConfigDescriptor(&len); | |||||
pbuf[1] = USB_DESC_TYPE_CONFIGURATION; | |||||
} | |||||
break; | |||||
case USB_DESC_TYPE_STRING: | |||||
switch ((uint8_t)(req->wValue)) | |||||
{ | |||||
case USBD_IDX_LANGID_STR: | |||||
pbuf = pdev->pDesc->GetLangIDStrDescriptor(pdev->dev_speed, &len); | |||||
break; | |||||
case USBD_IDX_MFC_STR: | |||||
pbuf = pdev->pDesc->GetManufacturerStrDescriptor(pdev->dev_speed, &len); | |||||
break; | |||||
case USBD_IDX_PRODUCT_STR: | |||||
pbuf = pdev->pDesc->GetProductStrDescriptor(pdev->dev_speed, &len); | |||||
break; | |||||
case USBD_IDX_SERIAL_STR: | |||||
pbuf = pdev->pDesc->GetSerialStrDescriptor(pdev->dev_speed, &len); | |||||
break; | |||||
case USBD_IDX_CONFIG_STR: | |||||
pbuf = pdev->pDesc->GetConfigurationStrDescriptor(pdev->dev_speed, &len); | |||||
break; | |||||
case USBD_IDX_INTERFACE_STR: | |||||
pbuf = pdev->pDesc->GetInterfaceStrDescriptor(pdev->dev_speed, &len); | |||||
break; | |||||
default: | |||||
#if (USBD_SUPPORT_USER_STRING == 1U) | |||||
pbuf = pdev->pClass->GetUsrStrDescriptor(pdev, (req->wValue) , &len); | |||||
break; | |||||
#else | |||||
USBD_CtlError(pdev , req); | |||||
return; | |||||
#endif | |||||
} | |||||
break; | |||||
case USB_DESC_TYPE_DEVICE_QUALIFIER: | |||||
if(pdev->dev_speed == USBD_SPEED_HIGH) | |||||
{ | |||||
pbuf = (uint8_t *)pdev->pClass->GetDeviceQualifierDescriptor(&len); | |||||
break; | |||||
} | |||||
else | |||||
{ | |||||
USBD_CtlError(pdev , req); | |||||
return; | |||||
} | |||||
case USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION: | |||||
if(pdev->dev_speed == USBD_SPEED_HIGH ) | |||||
{ | |||||
pbuf = (uint8_t *)pdev->pClass->GetOtherSpeedConfigDescriptor(&len); | |||||
pbuf[1] = USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION; | |||||
break; | |||||
} | |||||
else | |||||
{ | |||||
USBD_CtlError(pdev , req); | |||||
return; | |||||
} | |||||
default: | |||||
USBD_CtlError(pdev , req); | |||||
return; | |||||
} | |||||
if((len != 0U) && (req->wLength != 0U)) | |||||
{ | |||||
len = MIN(len, req->wLength); | |||||
USBD_CtlSendData (pdev, pbuf, len); | |||||
} | |||||
if(req->wLength == 0U) | |||||
{ | |||||
USBD_CtlSendStatus(pdev); | |||||
} | |||||
} | |||||
#endif | |||||
/** | |||||
* @brief USBD_SetAddress | |||||
* Set device address | |||||
* @param pdev: device instance | |||||
* @param req: usb request | |||||
* @retval status | |||||
*/ | |||||
static void USBD_SetAddress(USBD_HandleTypeDef *pdev , | |||||
USBD_SetupReqTypedef *req) | |||||
{ | |||||
uint8_t dev_addr; | |||||
if ((req->wIndex == 0U) && (req->wLength == 0U) && (req->wValue < 128U)) | |||||
{ | |||||
dev_addr = (uint8_t)(req->wValue) & 0x7FU; | |||||
if (pdev->dev_state == USBD_STATE_CONFIGURED) | |||||
{ | |||||
USBD_CtlError(pdev , req); | |||||
} | |||||
else | |||||
{ | |||||
pdev->dev_address = dev_addr; | |||||
USBD_LL_SetUSBAddress(pdev, dev_addr); | |||||
USBD_CtlSendStatus(pdev); | |||||
if (dev_addr != 0U) | |||||
{ | |||||
pdev->dev_state = USBD_STATE_ADDRESSED; | |||||
} | |||||
else | |||||
{ | |||||
pdev->dev_state = USBD_STATE_DEFAULT; | |||||
} | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
USBD_CtlError(pdev, req); | |||||
} | |||||
} | |||||
/** | |||||
* @brief USBD_SetConfig | |||||
* Handle Set device configuration request | |||||
* @param pdev: device instance | |||||
* @param req: usb request | |||||
* @retval status | |||||
*/ | |||||
static void USBD_SetConfig(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) | |||||
{ | |||||
static uint8_t cfgidx; | |||||
cfgidx = (uint8_t)(req->wValue); | |||||
if (cfgidx > USBD_MAX_NUM_CONFIGURATION) | |||||
{ | |||||
USBD_CtlError(pdev, req); | |||||
} | |||||
else | |||||
{ | |||||
switch (pdev->dev_state) | |||||
{ | |||||
case USBD_STATE_ADDRESSED: | |||||
if (cfgidx) | |||||
{ | |||||
pdev->dev_config = cfgidx; | |||||
pdev->dev_state = USBD_STATE_CONFIGURED; | |||||
if(USBD_SetClassConfig(pdev, cfgidx) == USBD_FAIL) | |||||
{ | |||||
USBD_CtlError(pdev, req); | |||||
return; | |||||
} | |||||
USBD_CtlSendStatus(pdev); | |||||
} | |||||
else | |||||
{ | |||||
USBD_CtlSendStatus(pdev); | |||||
} | |||||
break; | |||||
case USBD_STATE_CONFIGURED: | |||||
if (cfgidx == 0U) | |||||
{ | |||||
pdev->dev_state = USBD_STATE_ADDRESSED; | |||||
pdev->dev_config = cfgidx; | |||||
USBD_ClrClassConfig(pdev, cfgidx); | |||||
USBD_CtlSendStatus(pdev); | |||||
} | |||||
else if (cfgidx != pdev->dev_config) | |||||
{ | |||||
/* Clear old configuration */ | |||||
USBD_ClrClassConfig(pdev, (uint8_t)pdev->dev_config); | |||||
/* set new configuration */ | |||||
pdev->dev_config = cfgidx; | |||||
if(USBD_SetClassConfig(pdev, cfgidx) == USBD_FAIL) | |||||
{ | |||||
USBD_CtlError(pdev, req); | |||||
return; | |||||
} | |||||
USBD_CtlSendStatus(pdev); | |||||
} | |||||
else | |||||
{ | |||||
USBD_CtlSendStatus(pdev); | |||||
} | |||||
break; | |||||
default: | |||||
USBD_CtlError(pdev, req); | |||||
USBD_ClrClassConfig(pdev, cfgidx); | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* @brief USBD_GetConfig | |||||
* Handle Get device configuration request | |||||
* @param pdev: device instance | |||||
* @param req: usb request | |||||
* @retval status | |||||
*/ | |||||
static void USBD_GetConfig(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) | |||||
{ | |||||
if (req->wLength != 1U) | |||||
{ | |||||
USBD_CtlError(pdev , req); | |||||
} | |||||
else | |||||
{ | |||||
switch (pdev->dev_state) | |||||
{ | |||||
case USBD_STATE_DEFAULT: | |||||
case USBD_STATE_ADDRESSED: | |||||
pdev->dev_default_config = 0U; | |||||
USBD_CtlSendData (pdev, (uint8_t *)(void *)&pdev->dev_default_config, 1U); | |||||
break; | |||||
case USBD_STATE_CONFIGURED: | |||||
USBD_CtlSendData (pdev, (uint8_t *)(void *)&pdev->dev_config, 1U); | |||||
break; | |||||
default: | |||||
USBD_CtlError(pdev , req); | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* @brief USBD_GetStatus | |||||
* Handle Get Status request | |||||
* @param pdev: device instance | |||||
* @param req: usb request | |||||
* @retval status | |||||
*/ | |||||
static void USBD_GetStatus(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) | |||||
{ | |||||
switch (pdev->dev_state) | |||||
{ | |||||
case USBD_STATE_DEFAULT: | |||||
case USBD_STATE_ADDRESSED: | |||||
case USBD_STATE_CONFIGURED: | |||||
if(req->wLength != 0x2U) | |||||
{ | |||||
USBD_CtlError(pdev, req); | |||||
break; | |||||
} | |||||
#if ( USBD_SELF_POWERED == 1U) | |||||
pdev->dev_config_status = USB_CONFIG_SELF_POWERED; | |||||
#else | |||||
pdev->dev_config_status = 0U; | |||||
#endif | |||||
if (pdev->dev_remote_wakeup) | |||||
{ | |||||
pdev->dev_config_status |= USB_CONFIG_REMOTE_WAKEUP; | |||||
} | |||||
USBD_CtlSendData (pdev, (uint8_t *)(void *)&pdev->dev_config_status, 2U); | |||||
break; | |||||
default : | |||||
USBD_CtlError(pdev , req); | |||||
break; | |||||
} | |||||
} | |||||
/** | |||||
* @brief USBD_SetFeature | |||||
* Handle Set device feature request | |||||
* @param pdev: device instance | |||||
* @param req: usb request | |||||
* @retval status | |||||
*/ | |||||
static void USBD_SetFeature(USBD_HandleTypeDef *pdev , | |||||
USBD_SetupReqTypedef *req) | |||||
{ | |||||
if (req->wValue == USB_FEATURE_REMOTE_WAKEUP) | |||||
{ | |||||
pdev->dev_remote_wakeup = 1U; | |||||
USBD_CtlSendStatus(pdev); | |||||
} | |||||
} | |||||
/** | |||||
* @brief USBD_ClrFeature | |||||
* Handle clear device feature request | |||||
* @param pdev: device instance | |||||
* @param req: usb request | |||||
* @retval status | |||||
*/ | |||||
static void USBD_ClrFeature(USBD_HandleTypeDef *pdev , | |||||
USBD_SetupReqTypedef *req) | |||||
{ | |||||
switch (pdev->dev_state) | |||||
{ | |||||
case USBD_STATE_DEFAULT: | |||||
case USBD_STATE_ADDRESSED: | |||||
case USBD_STATE_CONFIGURED: | |||||
if (req->wValue == USB_FEATURE_REMOTE_WAKEUP) | |||||
{ | |||||
pdev->dev_remote_wakeup = 0U; | |||||
USBD_CtlSendStatus(pdev); | |||||
} | |||||
break; | |||||
default : | |||||
USBD_CtlError(pdev , req); | |||||
break; | |||||
} | |||||
} | |||||
/** | |||||
* @brief USBD_ParseSetupRequest | |||||
* Copy buffer into setup structure | |||||
* @param pdev: device instance | |||||
* @param req: usb request | |||||
* @retval None | |||||
*/ | |||||
void USBD_ParseSetupRequest(USBD_SetupReqTypedef *req, uint8_t *pdata) | |||||
{ | |||||
req->bmRequest = *(uint8_t *) (pdata); | |||||
req->bRequest = *(uint8_t *) (pdata + 1); | |||||
req->wValue = SWAPBYTE (pdata + 2); | |||||
req->wIndex = SWAPBYTE (pdata + 4); | |||||
req->wLength = SWAPBYTE (pdata + 6); | |||||
} | |||||
/** | |||||
* @brief USBD_CtlError | |||||
* Handle USB low level Error | |||||
* @param pdev: device instance | |||||
* @param req: usb request | |||||
* @retval None | |||||
*/ | |||||
void USBD_CtlError( USBD_HandleTypeDef *pdev , | |||||
USBD_SetupReqTypedef *req) | |||||
{ | |||||
USBD_LL_StallEP(pdev , 0x80U); | |||||
USBD_LL_StallEP(pdev , 0U); | |||||
} | |||||
/** | |||||
* @brief USBD_GetString | |||||
* Convert Ascii string into unicode one | |||||
* @param desc : descriptor buffer | |||||
* @param unicode : Formatted string buffer (unicode) | |||||
* @param len : descriptor length | |||||
* @retval None | |||||
*/ | |||||
void USBD_GetString(uint8_t *desc, uint8_t *unicode, uint16_t *len) | |||||
{ | |||||
uint8_t idx = 0U; | |||||
if (desc != NULL) | |||||
{ | |||||
*len = (uint16_t)USBD_GetLen(desc) * 2U + 2U; | |||||
unicode[idx++] = *(uint8_t *)(void *)len; | |||||
unicode[idx++] = USB_DESC_TYPE_STRING; | |||||
while (*desc != '\0') | |||||
{ | |||||
unicode[idx++] = *desc++; | |||||
unicode[idx++] = 0U; | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* @brief USBD_GetLen | |||||
* return the string length | |||||
* @param buf : pointer to the ascii string buffer | |||||
* @retval string length | |||||
*/ | |||||
static uint8_t USBD_GetLen(uint8_t *buf) | |||||
{ | |||||
uint8_t len = 0U; | |||||
while (*buf != '\0') | |||||
{ | |||||
len++; | |||||
buf++; | |||||
} | |||||
return len; | |||||
} | |||||
/** | |||||
* @} | |||||
*/ | |||||
/** | |||||
* @} | |||||
*/ | |||||
/** | |||||
* @} | |||||
*/ | |||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |
@@ -31,8 +31,6 @@ | |||||
#include <stm32l1xx.h> | #include <stm32l1xx.h> | ||||
#include <stm32l1xx_hal_flash_ex.h> | #include <stm32l1xx_hal_flash_ex.h> | ||||
#include <utilities.h> | |||||
#include <unistd.h> | #include <unistd.h> | ||||
#define nitems(x) (sizeof(x) / sizeof *(x)) | #define nitems(x) (sizeof(x) / sizeof *(x)) | ||||
@@ -66,6 +64,7 @@ strobe_rng_save(void) | |||||
{ | { | ||||
rng_word_t tmp[sizeof rng_save / sizeof(rng_word_t)]; | rng_word_t tmp[sizeof rng_save / sizeof(rng_word_t)]; | ||||
size_t i; | size_t i; | ||||
uint32_t primask; | |||||
int r; | int r; | ||||
/* | /* | ||||
@@ -74,7 +73,8 @@ strobe_rng_save(void) | |||||
r = strobe_randomize((uint8_t *)tmp, sizeof tmp); | r = strobe_randomize((uint8_t *)tmp, sizeof tmp); | ||||
(void)r; | (void)r; | ||||
CRITICAL_SECTION_BEGIN(); | |||||
primask = __get_PRIMASK(); | |||||
__disable_irq( ); | |||||
HAL_FLASHEx_DATAEEPROM_Unlock(); | HAL_FLASHEx_DATAEEPROM_Unlock(); | ||||
@@ -83,5 +83,5 @@ strobe_rng_save(void) | |||||
HAL_FLASHEx_DATAEEPROM_Lock(); | HAL_FLASHEx_DATAEEPROM_Lock(); | ||||
CRITICAL_SECTION_END(); | |||||
__set_PRIMASK(primask); | |||||
} | } |
@@ -3,10 +3,6 @@ | |||||
#define roundup(x, y) ((((x) + (y) - 1) / (y)) * (y)) | #define roundup(x, y) ((((x) + (y) - 1) / (y)) * (y)) | ||||
typedef uint32_t rng_word_t; | typedef uint32_t rng_word_t; | ||||
extern const rng_word_t rng_save[roundup(32, sizeof(rng_word_t)) / sizeof(rng_word_t)]; | |||||
void strobe_rng_init(void); | |||||
void strobe_rng_save(void); | |||||
static inline void | static inline void | ||||
bare_strobe_randomize(uint8_t *ptr, ssize_t len) | bare_strobe_randomize(uint8_t *ptr, ssize_t len) | ||||