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 | |||
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. | |||
@@ -55,3 +56,64 @@ D - Driver | |||
R - Receiver | |||
RO Receiver output | |||
/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 codecs | |||
import contextlib | |||
import functools | |||
import itertools | |||
import os | |||
import pathlib | |||
import sys | |||
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_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_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): | |||
'''Implement a LORANode initiator. | |||
@@ -140,7 +180,10 @@ class LORANode(object): | |||
def _encodeargs(*args): | |||
r = [] | |||
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) | |||
@@ -153,6 +196,10 @@ class LORANode(object): | |||
'response does not match, got: %s, expected: %s' % | |||
(repr(resp[0:1]), repr(cmdbyte))) | |||
r = resp[1:] | |||
if r: | |||
return r | |||
async def waitfor(self, length): | |||
return await self._sendcmd(CMD_WAITFOR, length) | |||
@@ -165,6 +212,14 @@ class LORANode(object): | |||
async def ping(self): | |||
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): | |||
args = () | |||
if cnt is not None: | |||
@@ -267,23 +322,52 @@ async def main(): | |||
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, | |||
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, | |||
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='*', | |||
help='Various commands to send to the device.') | |||
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: | |||
# Run a client | |||
mr = await multicast.create_multicast_receiver(maddr) | |||
mt = await multicast.create_multicast_transmitter(maddr) | |||
@@ -313,16 +397,20 @@ async def main(): | |||
cb = syote_comms.process_msgfunc_t(client_call) | |||
# Initialize everything | |||
syote_comms.comms_init(commstate, cb, make_pktbuf(shared_key)) | |||
syote_comms.comms_init(commstate, cb, *commsinitargs) | |||
try: | |||
while True: | |||
pkt = await mr.recv() | |||
msg = pkt[0] | |||
#_debprint('procmsg:', repr(msg)) | |||
out = syote_comms.comms_process_wrap( | |||
commstate, msg) | |||
#_debprint('resp:', repr(out)) | |||
if out: | |||
await mt.send(out) | |||
finally: | |||
@@ -333,13 +421,13 @@ async def main(): | |||
msd = MulticastSyncDatagram(maddr) | |||
await msd.start() | |||
l = LORANode(msd, shared=shared_key) | |||
l = LORANode(msd, **lorakwargs) | |||
await l.start() | |||
valid_cmds = { | |||
'waitfor', 'setunset', 'runfor', 'ping', 'adv', 'clear', | |||
'terminate', | |||
'terminate', 'type', | |||
} | |||
if args.args and args.schedfile: | |||
@@ -364,7 +452,10 @@ async def main(): | |||
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__': | |||
asyncio.run(main()) | |||
@@ -1096,7 +1187,7 @@ class TestLORANode(unittest.IsolatedAsyncioTestCase): | |||
@timeout(2) | |||
async def test_ccode(self): | |||
_self = self | |||
from ctypes import c_uint8 | |||
from ctypes import c_uint8, memmove | |||
# seed the RNG | |||
prngseed = b'abc123' | |||
@@ -1111,28 +1202,40 @@ class TestLORANode(unittest.IsolatedAsyncioTestCase): | |||
(CMD_WAITFOR, [ 30 ]), | |||
(CMD_RUNFOR, [ 1, 50 ]), | |||
(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, [ ]), | |||
] | |||
def procmsg(msg, outbuf): | |||
msgbuf = msg._from() | |||
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) | |||
outbuf[0].pkt[0] = cmd | |||
outbuf[0].pktlen = 1 | |||
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 | |||
cb = syote_comms.process_msgfunc_t(procmsg) | |||
class CCodeSD(MockSyncDatagram): | |||
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 | |||
inmsg = await self.get() | |||
@@ -1176,6 +1279,8 @@ class TestLORANode(unittest.IsolatedAsyncioTestCase): | |||
await l.ping() | |||
await l.type('sHt') | |||
await l.terminate() | |||
await tsd.drain() | |||
@@ -16,8 +16,14 @@ CFLAGS+= -Wall -Werror | |||
.include <$(.PARSEDIR)/mu.opts.mk> | |||
.if ${MK_HAL_INIT} == "yes" | |||
.PATH: $(SRCTOP)/board | |||
SRCS+= hal_init.c | |||
.endif | |||
.if ${MK_SYSINIT} == "yes" | |||
.PATH: $(SRCTOP) | |||
CFLAGS+= -I$(SRCTOP) | |||
SRCS+= sysinit.c | |||
.endif | |||
@@ -29,6 +35,11 @@ STROBE_SRCS+= strobe.c \ | |||
x25519.c | |||
.endif | |||
.if ${MK_STROBE} == "yes" && ${MK_STM32F103} == "yes" | |||
.PATH: $(SRCTOP)/board | |||
STROBE_SRCS+= strobe_f1_rng_init.c | |||
.endif | |||
# LoRamac (SX1276) radio code | |||
.if ${MK_SX1276} == "yes" | |||
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 | |||
.PATH: $(STM32)/f103c8t6 | |||
.PATH: $(SRCTOP)/board | |||
LINKER_SCRIPT=$(STM32)/f103c8t6/STM32F103C8T6_FLASH.ld | |||
SRCS+= \ | |||
misc.c \ | |||
f1_flash.c \ | |||
hal_generic.c \ | |||
startup_stm32f103xb.s \ | |||
stm32_bluepill.c \ | |||
stm32f1xx_hal.c \ | |||
stm32f1xx_hal_adc.c \ | |||
stm32f1xx_hal_adc_ex.c \ | |||
stm32f1xx_hal_cortex.c \ | |||
stm32f1xx_hal_dma.c \ | |||
stm32f1xx_hal_flash.c \ | |||
stm32f1xx_hal_flash_ex.c \ | |||
stm32f1xx_hal_gpio.c \ | |||
stm32f1xx_hal_pcd.c \ | |||
stm32f1xx_hal_pcd_ex.c \ | |||
@@ -65,7 +82,6 @@ SRCS+= \ | |||
stm32f1xx_hal_rcc.c \ | |||
stm32f1xx_hal_rcc_ex.c \ | |||
stm32f1xx_hal_uart.c \ | |||
stm32f1xx_ll_usb.c \ | |||
system_stm32f1xx.c | |||
CFLAGS+= -I$(STM32) | |||
@@ -123,8 +139,8 @@ CFLAGS+= -I$(SRCTOP)/rs485hid | |||
# USB CDC | |||
.if ${MK_USB_CDC} == "yes" | |||
.PATH: $(STM32)/usb | |||
SRCS+= \ | |||
si_usb.c \ | |||
SRCS.USB_CDC+= \ | |||
misc.c \ | |||
usb_device.c \ | |||
usbd_cdc.c \ | |||
usbd_cdc_if.c \ | |||
@@ -137,7 +153,28 @@ SRCS+= \ | |||
CFLAGS+= -I$(STM32)/usb | |||
.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 | |||
.endif |
@@ -2,6 +2,32 @@ | |||
# code. | |||
# | |||
# 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 \ | |||
SYSINIT | |||
@@ -11,8 +37,12 @@ __DEFAULT_NO_OPTIONS = \ | |||
RS485FRAME \ | |||
STM32F103 \ | |||
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> |
@@ -31,20 +31,14 @@ | |||
PROGEXT = .elf | |||
DEPENDS += .arm_deps | |||
.for i in $(PROGS) | |||
ALLTGTS+= $(i)$(PROGEXT) $(i).list | |||
ASRCS.$(i) = $(SRCS) $(SRCS.$(i)) | |||
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)) | |||
$(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) | |||
.endfor | |||
.PHONY: depend | |||
depend: $(DEPENDS) | |||
.arm_deps: $(ARM_DEP_SRCS) | |||
.arm_deps: | |||
$(ARMCC) $(ARMTARGET) $(CFLAGS) $(.ALLSRC) -MM > $@ || (rm -f $@ && false) | |||
.for i in $(DEPENDS) | |||
.sinclude "$i" | |||
.endfor | |||
@@ -61,7 +63,7 @@ all: $(ALLTGTS) | |||
.PHONY: runbuild | |||
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 | |||
.SUFFIXES: .no | |||
@@ -1,2 +1,3 @@ | |||
coverage | |||
strobe/python | |||
git+https://www.funkthat.com/gitea/jmg/python-usb-protocol.git |
@@ -22,20 +22,64 @@ | |||
# SUCH DAMAGE. | |||
# | |||
PROGS = rs485gw # lora.irr | |||
PROGS = rs485gw rs485hid | |||
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)/.. | |||
CFLAGS += -g | |||
CFLAGS += -I$(.CURDIR)/.. | |||
WITH_RS485FRAME = yes | |||
WITH_STM32F103 = yes | |||
WITH_USB_CDC = yes | |||
WITH_RS485FRAME = yes | |||
WITH_USB_HID = yes | |||
.include <../mk/boards.mk> | |||
EXTRA_DEPENDS+= $(STM32)/usb/usb_gen.py | |||
.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; | |||
static uint8_t rxbuf[1024]; | |||
static uint8_t rxbuf[128]; | |||
static txdone_fn_t txdone; | |||
static rxdone_fn_t rxdone; | |||
static txdone_fn_t errcb; | |||
@@ -62,7 +62,7 @@ settx(bool tx) | |||
else | |||
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_1, s); | |||
} | |||
@@ -71,11 +71,19 @@ void | |||
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 */ | |||
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) { | |||
HAL_UART_AbortReceive_IT(&rs485uart); | |||
} | |||
#endif | |||
settx(true); | |||
@@ -99,6 +107,8 @@ HAL_UART_TxCpltCallback(UART_HandleTypeDef *phuart) | |||
if (phuart != &rs485uart || txdone == NULL) | |||
return; | |||
for (int x = 0; x < 1000000; x++); | |||
settx(false); | |||
doingtx = 0; | |||
@@ -140,12 +150,13 @@ HAL_UART_MspInit(UART_HandleTypeDef *huart) | |||
/* UM1850 § 38.1.2 Page 550 */ | |||
/* 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(); | |||
/* 2.b */ | |||
__HAL_RCC_GPIOB_CLK_ENABLE(); | |||
/* TX pin */ | |||
GPIO_InitStruct = (GPIO_InitTypeDef){ | |||
.Pin = GPIO_PIN_10, | |||
@@ -197,10 +208,17 @@ rs485_startrx() | |||
return 0; | |||
} | |||
doingrx = 1; | |||
r = HAL_UARTEx_ReceiveToIdle_IT(&rs485uart, rxbuf, sizeof rxbuf); | |||
if (r == HAL_BUSY) { | |||
rs485err = "already"; | |||
return 1; | |||
#if 0 | |||
/* This appears to corrupt state and break things */ | |||
HAL_UART_AbortReceive_IT(&rs485uart); | |||
r = HAL_UARTEx_ReceiveToIdle_IT(&rs485uart, rxbuf, sizeof rxbuf); | |||
#endif | |||
} | |||
rs485err = statustostr(r); | |||
@@ -209,14 +227,16 @@ rs485_startrx() | |||
} | |||
static void | |||
rs485frame_init(const void *v) | |||
rs485frame_init(void) | |||
{ | |||
GPIO_InitTypeDef GPIO_InitStruct; | |||
__HAL_RCC_GPIOB_CLK_ENABLE(); | |||
/* setup DE/RE */ | |||
GPIO_InitStruct = (GPIO_InitTypeDef){ | |||
.Pin = GPIO_PIN_0|GPIO_PIN_1, | |||
.Mode = GPIO_MODE_OUTPUT_OD, | |||
.Mode = GPIO_MODE_OUTPUT_PP, | |||
.Pull = GPIO_NOPULL, | |||
.Speed = GPIO_SPEED_FREQ_LOW, /* 2 MHz */ | |||
}; | |||
@@ -240,4 +260,4 @@ rs485frame_init(const void *v) | |||
/* 4 */ | |||
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> | |||
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 uint8_t rxbuf[128]; | |||
@@ -71,51 +82,6 @@ SYSINIT(c13led, SI_SUB_HAL, SI_ORDER_SECOND, c13led, NULL); | |||
#define usb_printf debug_printf | |||
#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 * | |||
findeol(char *pos, size_t len) | |||
{ | |||
@@ -167,19 +133,6 @@ rxdone(const uint8_t *payload, size_t size) | |||
gotrxdone = 1; | |||
} | |||
void | |||
rxtimeout(void) | |||
{ | |||
usb_printf("rxtimeout\r\n"); | |||
} | |||
void | |||
rxerr(void) | |||
{ | |||
usb_printf("rxerr\r\n"); | |||
} | |||
static uint8_t | |||
hexchartonib(char s) | |||
{ | |||
@@ -278,7 +231,7 @@ main(void) | |||
debug_printf("starting...\n"); | |||
#if 1 | |||
#if 0 | |||
int i; | |||
for (i = 0; i < 5; i++) { | |||
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 <stm32f1xx_hal_gpio.h> | |||
#include <stm32f1xx_hal_pcd.h> | |||
#include <usb_device.h> | |||
static void | |||
usb_cdc_init(const void *foo) | |||
usb_reset(void) | |||
{ | |||
/* | |||
* 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_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_Stack_Size = 0x400 ; /* required amount of stack */ | |||
_flash_page_size = 0x400 ; /* size of a flash page */ | |||
/* Memories definition */ | |||
MEMORY | |||
{ | |||
@@ -100,7 +102,6 @@ SECTIONS | |||
__start_set_sysinit_set = .; | |||
KEEP(*(set_sysinit_set)) /* sysinit linker sets */ | |||
__stop_set_sysinit_set = .; | |||
. = ALIGN(4); | |||
} >FLASH | |||
.ARM.extab : { | |||
@@ -162,6 +163,20 @@ SECTIONS | |||
} >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 */ | |||
. = ALIGN(4); | |||
.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_hal_flash_ex.h> | |||
#include <utilities.h> | |||
#include <unistd.h> | |||
#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)]; | |||
size_t i; | |||
uint32_t primask; | |||
int r; | |||
/* | |||
@@ -74,7 +73,8 @@ strobe_rng_save(void) | |||
r = strobe_randomize((uint8_t *)tmp, sizeof tmp); | |||
(void)r; | |||
CRITICAL_SECTION_BEGIN(); | |||
primask = __get_PRIMASK(); | |||
__disable_irq( ); | |||
HAL_FLASHEx_DATAEEPROM_Unlock(); | |||
@@ -83,5 +83,5 @@ strobe_rng_save(void) | |||
HAL_FLASHEx_DATAEEPROM_Lock(); | |||
CRITICAL_SECTION_END(); | |||
__set_PRIMASK(primask); | |||
} |
@@ -3,10 +3,6 @@ | |||
#define roundup(x, y) ((((x) + (y) - 1) / (y)) * (y)) | |||
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 | |||
bare_strobe_randomize(uint8_t *ptr, ssize_t len) | |||