Browse Source

big drop adding the USB HID part of this project...

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
John-Mark Gurney 2 years ago
parent
commit
4d369b9517
27 changed files with 2824 additions and 135 deletions
  1. +63
    -1
      RS485HID.md
  2. +41
    -0
      board/f1_flash.c
  3. +5
    -0
      board/hal_init.c
  4. +1
    -0
      board/simpflash.h
  5. +62
    -0
      board/stm32_bluepill.c
  6. +142
    -0
      board/strobe_f1_rng_init.c
  7. +123
    -18
      lora.py
  8. +43
    -6
      mk/boards.mk
  9. +32
    -2
      mk/mu.opts.mk
  10. +12
    -10
      mk/mu.progs.mk
  11. +1
    -0
      requirements.txt
  12. +48
    -4
      rs485hid/Makefile
  13. +65
    -0
      rs485hid/memdebug.c
  14. +28
    -8
      rs485hid/rs485frame.c
  15. +13
    -60
      rs485hid/rs485gw.c
  16. +558
    -0
      rs485hid/rs485hid.c
  17. +87
    -0
      rs485hid/strobe_pki.c
  18. +9
    -0
      rs485hid/strobe_pki.h
  19. +62
    -0
      rs485hid/usb_hid.py
  20. +328
    -0
      rs485hid/usb_hid_def.c
  21. +6
    -0
      rs485hid/usb_hid_def.h
  22. +6
    -17
      si_usb.c
  23. +16
    -1
      stm32/f103c8t6/STM32F103C8T6_FLASH.ld
  24. +216
    -0
      stm32/usb/usb_gen.py
  25. +853
    -0
      stm32/usb/usbd_ctlreq_nodesc.c
  26. +4
    -4
      strobe_rng_init.c
  27. +0
    -4
      strobe_rng_init.h

+ 63
- 1
RS485HID.md View File

@@ -18,7 +18,8 @@ These are the device that are under conideration for the project:


### STM32F103C8 ### STM32F103C8


64KB flash
64KB flash, but the devices I have seem to have 128KB flash. They
appear to be BluePill clones. Green LED is GPIOC Pin 13.


Currently looking at USART3, B0 for DE, B1 for RE. Currently looking at USART3, B0 for DE, B1 for RE.


@@ -55,3 +56,64 @@ D - Driver
R - Receiver R - Receiver
RO Receiver output RO Receiver output
/RE Receiver enable (low enable) /RE Receiver enable (low enable)

Note: Some pre-assembled devices have pull up resistors on the DE/RE
lines. This is a BAD design as it means that it will be driving the
bus by default. This can often be fixed by removing the pull-up
resistors.


Programming
-----------

Command to program the HID device:
```
sudo openocd -f interface/ftdi/digilent-hs1.cfg -f interface/ftdi/swd-resistor-hack.cfg -f target/stm32f1x.cfg -c "init" -c "reset init" -c "program build/rs485hid.elf verify reset exit"
```

Keying
------

There are two keys involved, the initiator key, which is made w/ the
Makefile, but issuing the command:
```
bmake hid_priv_key
```

Which creates the private key in the file `.hid_priv_key`. This is
needed for the build process.

The other key is the device key, which is generated by the USB HID
device at first start. The device will "type" the hex encoded public
key when the A7 pin is grounded. This public key should be saved to
a file, which can then be passed to the `lora.py` initiator program
via the `-p` argument.

Low level info
--------------

Definitions:
Mark: logic 1, B > A
Space: logic 0, B < A

Idle state: Mark

Async comms: Space | 8 bits LSB, 0 through 7 | Mark

As the start of the sequence begins w/ a space, it requires that the
line be "idle" (aka mark) before things start, so any non-mark state
before TX starts should consider the line as busy, and not be ready
to transmit. Additional info on this is in the Wiring section.

Wiring
------

For long runs, it is recommend to have terminating resistors w/ a value
of 120 Ω, the impedence of twisted pair, to prevent reflections.

Wikipedia recommends to add biasing resistors to help w/ noise
immunity, BUT, care must be done when using them. When the bus is
idle, make sure that the receivers are outputing a hi value (aka
mark), that is 5V if you're using a MAX485 converter. If it is 0V,
then the receiver will not work. I have seen in some cases where
grounding A w/ a 2.2k Ω resistor makes things work.

+ 41
- 0
board/f1_flash.c View File

@@ -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);
}

+ 5
- 0
board/hal_init.c View File

@@ -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);

+ 1
- 0
board/simpflash.h View File

@@ -0,0 +1 @@
void doflash(const void *dst, void *src, int bytes);

+ 62
- 0
board/stm32_bluepill.c View File

@@ -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);

+ 142
- 0
board/strobe_f1_rng_init.c View File

@@ -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);

+ 123
- 18
lora.py View File

@@ -23,10 +23,12 @@
# #


import asyncio import asyncio
import codecs
import contextlib import contextlib
import functools import functools
import itertools import itertools
import os import os
import pathlib
import sys import sys
import unittest import unittest


@@ -49,6 +51,44 @@ CMD_PING = 4 # arg: (): a no op command
CMD_SETUNSET = 5 # arg: (chan, val): sets chan to val CMD_SETUNSET = 5 # arg: (chan, val): sets chan to val
CMD_ADV = 6 # arg: ([cnt]): advances to the next cnt (default 1) command CMD_ADV = 6 # arg: ([cnt]): advances to the next cnt (default 1) command
CMD_CLEAR = 7 # arg: (): clears all future commands, but keeps current running CMD_CLEAR = 7 # arg: (): clears all future commands, but keeps current running
CMD_KEY = 8 # arg: ([key reports]): standard USB HID key reports, return is number added

_directmap = [
(40, '\n\x1b\b\t -=[]\\#;\'`,./'),
(40, '\r'),
]
_keymap = { chr(x): x - ord('a') + 4 for x in range(ord('a'), ord('z') + 1) } | \
{ '0': 39 } | \
{ chr(x): x - ord('1') + 30 for x in range(ord('1'), ord('9') + 1) } | \
{ x: i + base for base, keys in _directmap for i, x in enumerate(keys) }
_shiftmaplist = '!1@2#3$4%5^6&7*8(9)0_-+=~`{[}]|\\:;"\'<,>.?/'
_completemap = { x: (False, y) for x, y in _keymap.items() } | \
{ x.upper(): (True, y) for x, y in _keymap.items() if x != x.upper() } | \
{ x: (True, _keymap[y]) for x, y in zip(_shiftmaplist[::2], _shiftmaplist[1::2]) }


def makekeybuf(s):
blank = b'\x00' * 8

# start clean
ret = [ blank ]
templ = bytearray([ 0 ] * 8)

for i in s:
shift, key = _completemap[i]

if ret[-1][2] == key:
ret.append(blank)

templ[2] = key
templ[0] = 2 if shift else 0

ret.append(templ[:])

# end clean
ret.append(blank)

return b''.join(ret)


class LORANode(object): class LORANode(object):
'''Implement a LORANode initiator. '''Implement a LORANode initiator.
@@ -140,7 +180,10 @@ class LORANode(object):
def _encodeargs(*args): def _encodeargs(*args):
r = [] r = []
for i in args: for i in args:
r.append(i.to_bytes(4, byteorder='little'))
if isinstance(i, bytes):
r.append(i)
else:
r.append(i.to_bytes(4, byteorder='little'))


return b''.join(r) return b''.join(r)


@@ -153,6 +196,10 @@ class LORANode(object):
'response does not match, got: %s, expected: %s' % 'response does not match, got: %s, expected: %s' %
(repr(resp[0:1]), repr(cmdbyte))) (repr(resp[0:1]), repr(cmdbyte)))


r = resp[1:]
if r:
return r

async def waitfor(self, length): async def waitfor(self, length):
return await self._sendcmd(CMD_WAITFOR, length) return await self._sendcmd(CMD_WAITFOR, length)


@@ -165,6 +212,14 @@ class LORANode(object):
async def ping(self): async def ping(self):
return await self._sendcmd(CMD_PING) return await self._sendcmd(CMD_PING)


async def type(self, s):
keys = makekeybuf(s)

while keys:
r = await self._sendcmd(CMD_KEY, keys[:8 * 6])
r = int.from_bytes(r, byteorder='little')
keys = keys[r * 8:]

async def adv(self, cnt=None): async def adv(self, cnt=None):
args = () args = ()
if cnt is not None: if cnt is not None:
@@ -267,23 +322,52 @@ async def main():


from loraserv import DEFAULT_MADDR as maddr from loraserv import DEFAULT_MADDR as maddr


parser = argparse.ArgumentParser()
parser = argparse.ArgumentParser(description='This is an implementation of both the server and client implementing syote secure IoT protocol.',
epilog='Both -k and -p MUST be used together. One of either -s or the combone -k & -p MUST be specified.')


parser.add_argument('-f', dest='schedfile', metavar='filename', type=str, parser.add_argument('-f', dest='schedfile', metavar='filename', type=str,
help='Use commands from the file. One command per line.') help='Use commands from the file. One command per line.')
parser.add_argument('-k', dest='privkey', metavar='privfile', type=str,
help='File containing a hex encoded private key.')
parser.add_argument('-p', dest='pubkey', metavar='pubfile', type=str,
help='File containing a hex encoded public key.')
parser.add_argument('-r', dest='client', metavar='module:function', type=str, parser.add_argument('-r', dest='client', metavar='module:function', type=str,
help='Create a respondant instead of sending commands. Commands will be passed to the function.') help='Create a respondant instead of sending commands. Commands will be passed to the function.')
parser.add_argument('-s', dest='shared_key', metavar='shared_key', type=str, required=True,
help='The shared key (encoded as UTF-8) to use.')
parser.add_argument('-s', dest='shared_key', metavar='shared_key', type=str,
help='File containing the shared key to use (note, any white space is included).')
parser.add_argument('args', metavar='CMD_ARG', type=str, nargs='*', parser.add_argument('args', metavar='CMD_ARG', type=str, nargs='*',
help='Various commands to send to the device.') help='Various commands to send to the device.')


args = parser.parse_args() args = parser.parse_args()


shared_key = args.shared_key.encode('utf-8')
# make sure a key is specified.
if args.privkey is None and args.pubkey is None and \
args.shared_key is None:
parser.error('a key must be specified (either -k and -p, or -s)')

# make sure if one is specified, both are.
if (args.privkey is not None or args.pubkey is not None) and \
(args.privkey is None or args.pubkey is None):
parser.error('both -k and -p MUST be specified if one is.')

if args.shared_key is not None:
skdata = pathlib.Path(args.shared_key).read_bytes()
lorakwargs = dict(shared_key=skdata)
commsinitargs = (lorakwargs['shared_key'], )
else:
privkeydata = pathlib.Path(args.privkey).read_text().strip()
privkey = X25519.frombytes(codecs.decode(privkeydata, 'hex'))

pubkeydata = pathlib.Path(args.pubkey).read_text().strip()
pubkey = codecs.decode(pubkeydata, 'hex')

lorakwargs = dict(init_key=privkey, resp_pub=pubkey)
commsinitargs = (None, make_pktbuf(privkey.getpriv()),
make_pktbuf(pubkey))


if args.client: if args.client:
# Run a client # Run a client

mr = await multicast.create_multicast_receiver(maddr) mr = await multicast.create_multicast_receiver(maddr)
mt = await multicast.create_multicast_transmitter(maddr) mt = await multicast.create_multicast_transmitter(maddr)


@@ -313,16 +397,20 @@ async def main():
cb = syote_comms.process_msgfunc_t(client_call) cb = syote_comms.process_msgfunc_t(client_call)


# Initialize everything # Initialize everything
syote_comms.comms_init(commstate, cb, make_pktbuf(shared_key))
syote_comms.comms_init(commstate, cb, *commsinitargs)


try: try:
while True: while True:
pkt = await mr.recv() pkt = await mr.recv()
msg = pkt[0] msg = pkt[0]


#_debprint('procmsg:', repr(msg))

out = syote_comms.comms_process_wrap( out = syote_comms.comms_process_wrap(
commstate, msg) commstate, msg)


#_debprint('resp:', repr(out))

if out: if out:
await mt.send(out) await mt.send(out)
finally: finally:
@@ -333,13 +421,13 @@ async def main():
msd = MulticastSyncDatagram(maddr) msd = MulticastSyncDatagram(maddr)
await msd.start() await msd.start()


l = LORANode(msd, shared=shared_key)
l = LORANode(msd, **lorakwargs)


await l.start() await l.start()


valid_cmds = { valid_cmds = {
'waitfor', 'setunset', 'runfor', 'ping', 'adv', 'clear', 'waitfor', 'setunset', 'runfor', 'ping', 'adv', 'clear',
'terminate',
'terminate', 'type',
} }


if args.args and args.schedfile: if args.args and args.schedfile:
@@ -364,7 +452,10 @@ async def main():


fun = getattr(l, cmd) fun = getattr(l, cmd)


await fun(*(int(x) for x in args))
try:
await fun(*(int(x) for x in args))
except:
await fun(*args)


if __name__ == '__main__': if __name__ == '__main__':
asyncio.run(main()) asyncio.run(main())
@@ -1096,7 +1187,7 @@ class TestLORANode(unittest.IsolatedAsyncioTestCase):
@timeout(2) @timeout(2)
async def test_ccode(self): async def test_ccode(self):
_self = self _self = self
from ctypes import c_uint8
from ctypes import c_uint8, memmove


# seed the RNG # seed the RNG
prngseed = b'abc123' prngseed = b'abc123'
@@ -1111,28 +1202,40 @@ class TestLORANode(unittest.IsolatedAsyncioTestCase):
(CMD_WAITFOR, [ 30 ]), (CMD_WAITFOR, [ 30 ]),
(CMD_RUNFOR, [ 1, 50 ]), (CMD_RUNFOR, [ 1, 50 ]),
(CMD_PING, [ ]), (CMD_PING, [ ]),
# using big here, because Python is stupid.
(CMD_KEY, b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00\x00\x00\x00\x02\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x17\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', CMD_KEY.to_bytes(1, byteorder='big') + (3).to_bytes(4, byteorder='little')),
(CMD_KEY, b'\x00\x00\x17\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', CMD_KEY.to_bytes(1, byteorder='big') + (2).to_bytes(4, byteorder='little')),
(CMD_TERMINATE, [ ]), (CMD_TERMINATE, [ ]),
] ]
def procmsg(msg, outbuf): def procmsg(msg, outbuf):
msgbuf = msg._from() msgbuf = msg._from()
cmd = msgbuf[0] cmd = msgbuf[0]
args = [ int.from_bytes(msgbuf[x:x + 4],
byteorder='little') for x in range(1, len(msgbuf),
4) ]
if isinstance(exptmsgs[0][1], bytes):
args = msgbuf[1:]
else:
args = [ int.from_bytes(msgbuf[x:x + 4],
byteorder='little') for x in range(1, len(msgbuf),
4) ]

if exptmsgs[0][:2] == (cmd, args):
if len(exptmsgs[0]) > 2:
rmsg = exptmsgs[0][2]
memmove(outbuf[0].pkt, rmsg, len(rmsg))
outbuf[0].pktlen = len(rmsg)
else:
outbuf[0].pkt[0] = cmd
outbuf[0].pktlen = 1


if exptmsgs[0] == (cmd, args):
exptmsgs.pop(0) exptmsgs.pop(0)
outbuf[0].pkt[0] = cmd
outbuf[0].pktlen = 1
else: #pragma: no cover else: #pragma: no cover
raise RuntimeError('cmd not found')
raise RuntimeError('cmd not found, got %d, expected %d' % (cmd, exptmsgs[0][0]))


# wrap the callback function # wrap the callback function
cb = syote_comms.process_msgfunc_t(procmsg) cb = syote_comms.process_msgfunc_t(procmsg)


class CCodeSD(MockSyncDatagram): class CCodeSD(MockSyncDatagram):
async def runner(self): async def runner(self):
for expectlen in [ 24, 17, 9, 9, 9, 9 ]:
for expectlen in [ 24, 17, 9, 9, 9, 13, 13, 9 ]:
# get message # get message
inmsg = await self.get() inmsg = await self.get()


@@ -1176,6 +1279,8 @@ class TestLORANode(unittest.IsolatedAsyncioTestCase):


await l.ping() await l.ping()


await l.type('sHt')

await l.terminate() await l.terminate()


await tsd.drain() await tsd.drain()


+ 43
- 6
mk/boards.mk View File

@@ -16,8 +16,14 @@ CFLAGS+= -Wall -Werror


.include <$(.PARSEDIR)/mu.opts.mk> .include <$(.PARSEDIR)/mu.opts.mk>


.if ${MK_HAL_INIT} == "yes"
.PATH: $(SRCTOP)/board
SRCS+= hal_init.c
.endif

.if ${MK_SYSINIT} == "yes" .if ${MK_SYSINIT} == "yes"
.PATH: $(SRCTOP) .PATH: $(SRCTOP)
CFLAGS+= -I$(SRCTOP)
SRCS+= sysinit.c SRCS+= sysinit.c
.endif .endif


@@ -29,6 +35,11 @@ STROBE_SRCS+= strobe.c \
x25519.c x25519.c
.endif .endif


.if ${MK_STROBE} == "yes" && ${MK_STM32F103} == "yes"
.PATH: $(SRCTOP)/board
STROBE_SRCS+= strobe_f1_rng_init.c
.endif

# LoRamac (SX1276) radio code # LoRamac (SX1276) radio code
.if ${MK_SX1276} == "yes" .if ${MK_SX1276} == "yes"
LORAMAC_SRC = $(SRCTOP)/loramac/src LORAMAC_SRC = $(SRCTOP)/loramac/src
@@ -49,15 +60,21 @@ SRCS+= adc-board.c delay-board.c gpio-board.c rtc-board.c lpm-board.c sx1276mb1l
ARMTARGET?= -mcpu=cortex-m3 -mthumb ARMTARGET?= -mcpu=cortex-m3 -mthumb


.PATH: $(STM32)/f103c8t6 .PATH: $(STM32)/f103c8t6
.PATH: $(SRCTOP)/board
LINKER_SCRIPT=$(STM32)/f103c8t6/STM32F103C8T6_FLASH.ld LINKER_SCRIPT=$(STM32)/f103c8t6/STM32F103C8T6_FLASH.ld


SRCS+= \ SRCS+= \
misc.c \
f1_flash.c \
hal_generic.c \ hal_generic.c \
startup_stm32f103xb.s \ startup_stm32f103xb.s \
stm32_bluepill.c \
stm32f1xx_hal.c \ stm32f1xx_hal.c \
stm32f1xx_hal_adc.c \
stm32f1xx_hal_adc_ex.c \
stm32f1xx_hal_cortex.c \ stm32f1xx_hal_cortex.c \
stm32f1xx_hal_dma.c \ stm32f1xx_hal_dma.c \
stm32f1xx_hal_flash.c \
stm32f1xx_hal_flash_ex.c \
stm32f1xx_hal_gpio.c \ stm32f1xx_hal_gpio.c \
stm32f1xx_hal_pcd.c \ stm32f1xx_hal_pcd.c \
stm32f1xx_hal_pcd_ex.c \ stm32f1xx_hal_pcd_ex.c \
@@ -65,7 +82,6 @@ SRCS+= \
stm32f1xx_hal_rcc.c \ stm32f1xx_hal_rcc.c \
stm32f1xx_hal_rcc_ex.c \ stm32f1xx_hal_rcc_ex.c \
stm32f1xx_hal_uart.c \ stm32f1xx_hal_uart.c \
stm32f1xx_ll_usb.c \
system_stm32f1xx.c system_stm32f1xx.c


CFLAGS+= -I$(STM32) CFLAGS+= -I$(STM32)
@@ -123,8 +139,8 @@ CFLAGS+= -I$(SRCTOP)/rs485hid
# USB CDC # USB CDC
.if ${MK_USB_CDC} == "yes" .if ${MK_USB_CDC} == "yes"
.PATH: $(STM32)/usb .PATH: $(STM32)/usb
SRCS+= \
si_usb.c \
SRCS.USB_CDC+= \
misc.c \
usb_device.c \ usb_device.c \
usbd_cdc.c \ usbd_cdc.c \
usbd_cdc_if.c \ usbd_cdc_if.c \
@@ -137,7 +153,28 @@ SRCS+= \
CFLAGS+= -I$(STM32)/usb CFLAGS+= -I$(STM32)/usb
.endif .endif


.if ${MK_USB_CDC} == "yes" && ${MK_NODE151} == "yes"
SRCS+= \
# USB HID
.if ${MK_USB_HID} == "yes"
.PATH: $(STM32)/usb
CFLAGS+= -I$(.OBJDIR)
SRCS.USB_HID+= \
usb_hid_def.c \
usbd_conf.c \
usbd_core.c \
usbd_ctlreq_nodesc.c \
usbd_ioreq.c

CFLAGS+= -I$(STM32)/usb
.endif

.if ${MK_USB} == "yes" && ${MK_STM32F103} == "yes"
SRCS.USB+= \
si_usb.c \
stm32f1xx_ll_usb.c
.endif

.if ${MK_USB} == "yes" && ${MK_NODE151} == "yes"
SRCS.USB_CDC+= \
si_usb.c \
stm32l1xx_ll_usb.c stm32l1xx_ll_usb.c
.endif .endif

+ 32
- 2
mk/mu.opts.mk View File

@@ -2,6 +2,32 @@
# code. # code.
# #
# See bsd.mkopt.mk for more information. # See bsd.mkopt.mk for more information.
#
# An option should only be listed in one of the following sections.
# If it's listed as a dependent option, do NOT list it in a default
# section.
#

#
# If the option doesn't state if it defines a SRCS var, then
# the necessary sources are added to the SRCS var.
#
# Options (by order in this file):
#
# STROBE - The STROBE crypto library. (Defines STROBE_SRCS)
# SYSINIT - The sysinit framework. Needed to run the init code.
#
# NODE151 - Code needed for the Node151 LoRa module
# RS485FRAME - Base code for RS485 framing tx/rx
# STM32F103 - Base code for the BluePill microcontroller module
# SX1276 - LoRa radio code, for Node151
# USB_CDC - Code implementing the CDC Device mode. (Defines SRCS.USB_CDC)
# USB_HID - Code implementing the HID Device mode. (Defines SRCS.USB_HID)
#
# Dependent options:
# HAL_INIT - Run hal_init via sysinit.
# USB - Turn on generic USB support code. (Defines SRCS.USB)
#


__DEFAULT_YES_OPTIONS = STROBE \ __DEFAULT_YES_OPTIONS = STROBE \
SYSINIT SYSINIT
@@ -11,8 +37,12 @@ __DEFAULT_NO_OPTIONS = \
RS485FRAME \ RS485FRAME \
STM32F103 \ STM32F103 \
SX1276 \ SX1276 \
USB_CDC
USB_CDC \
USB_HID


__DEFAULT_DEPENDENT_OPTIONS =
__DEFAULT_DEPENDENT_OPTIONS = \
HAL_INIT/STM32F103 \
USB/USB_CDC \
USB/USB_HID


.include <$(.PARSEDIR)/bsd.mkopt.mk> .include <$(.PARSEDIR)/bsd.mkopt.mk>

+ 12
- 10
mk/mu.progs.mk View File

@@ -31,20 +31,14 @@


PROGEXT = .elf PROGEXT = .elf


DEPENDS += .arm_deps

.for i in $(PROGS) .for i in $(PROGS)
ALLTGTS+= $(i)$(PROGEXT) $(i).list ALLTGTS+= $(i)$(PROGEXT) $(i).list
ASRCS.$(i) = $(SRCS) $(SRCS.$(i)) ASRCS.$(i) = $(SRCS) $(SRCS.$(i))
OBJS.$(i) = $(ASRCS.$(i):C/.c$/.o/) OBJS.$(i) = $(ASRCS.$(i):C/.c$/.o/)


DEPENDS += .arm_deps

.arm_deps: $(ASRCS.$(i))

.arm_deps:
$(ARMCC) $(ARMTARGET) $(CFLAGS) $(.ALLSRC) -MM > $@ || (rm -f $@ && false)

.PHONY: depend
depend: $(DEPENDS)
ARM_DEP_SRCS+= $(ASRCS.$(i))


$(i)$(PROGEXT) $(i).map: $(OBJS.$(i)) $(i)$(PROGEXT) $(i).map: $(OBJS.$(i))
$(ARMCC) $(ARMTARGET) -o $(i)$(PROGEXT) $(.ALLSRC) -T$(LINKER_SCRIPT) --specs=nosys.specs -Wl,-Map="$(i).map" -Wl,--gc-sections -static --specs=nano.specs -Wl,--start-group -lc -lm -Wl,--end-group $(ARMCC) $(ARMTARGET) -o $(i)$(PROGEXT) $(.ALLSRC) -T$(LINKER_SCRIPT) --specs=nosys.specs -Wl,-Map="$(i).map" -Wl,--gc-sections -static --specs=nano.specs -Wl,--start-group -lc -lm -Wl,--end-group
@@ -53,6 +47,14 @@ $(i).list: $(i)$(PROGEXT)
$(ARMOBJDUMP) -h -S $(.ALLSRC) > $@ || (rm -f $@ && false) $(ARMOBJDUMP) -h -S $(.ALLSRC) > $@ || (rm -f $@ && false)
.endfor .endfor


.PHONY: depend
depend: $(DEPENDS)

.arm_deps: $(ARM_DEP_SRCS)

.arm_deps:
$(ARMCC) $(ARMTARGET) $(CFLAGS) $(.ALLSRC) -MM > $@ || (rm -f $@ && false)

.for i in $(DEPENDS) .for i in $(DEPENDS)
.sinclude "$i" .sinclude "$i"
.endfor .endfor
@@ -61,7 +63,7 @@ all: $(ALLTGTS)


.PHONY: runbuild .PHONY: runbuild
runbuild: $(SRCS) Makefile mk/*.mk runbuild: $(SRCS) Makefile mk/*.mk
for i in $(.MAKEFILE_LIST) $(.ALLSRC) $$(cat $(DEPENDS) | gsed ':x; /\\$$/ { N; s/\\\n//; tx }' | sed -e 's/^[^:]*://'); do if [ "$$i" != ".." ]; then echo $$i; fi; done | sort -u | entr -d sh -c 'echo starting...; cd $(.CURDIR) && $(MAKE) $(.MAKEFLAGS) depend && $(MAKE) $(.MAKEFLAGS) all'
for i in $(EXTRA_DEPENDS) $(.MAKEFILE_LIST) $(.ALLSRC) $$(cat $(DEPENDS) | gsed ':x; /\\$$/ { N; s/\\\n//; tx }' | sed -e 's/^[^:]*://'); do if [ "$$i" != ".." ]; then echo $$i; fi; done | sort -u | entr -d sh -c 'echo starting...; cd $(.CURDIR) && $(MAKE) $(.MAKEFLAGS) depend && $(MAKE) $(.MAKEFLAGS) all'


# native objects # native objects
.SUFFIXES: .no .SUFFIXES: .no


+ 1
- 0
requirements.txt View File

@@ -1,2 +1,3 @@
coverage coverage
strobe/python strobe/python
git+https://www.funkthat.com/gitea/jmg/python-usb-protocol.git

+ 48
- 4
rs485hid/Makefile View File

@@ -22,20 +22,64 @@
# SUCH DAMAGE. # SUCH DAMAGE.
# #


PROGS = rs485gw # lora.irr
PROGS = rs485gw rs485hid


SRCS.rs485gw = rs485gw.c SRCS.rs485gw = rs485gw.c
SRCS.rs485gw+= misc.c
SRCS.rs485gw+= $(SRCS.USB_CDC)
SRCS.rs485gw+= $(SRCS.USB)

SRCS.rs485hid = rs485hid.c
SRCS.rs485hid+= $(SRCS.USB_HID)
SRCS.rs485hid+= $(SRCS.USB)
SRCS.rs485hid+= $(STROBE_SRCS)
SRCS.rs485hid+= comms.c
SRCS.rs485hid+= memdebug.c
SRCS.rs485hid+= strobe_pki.c

CFLAGS.rs485hid+= -DEXTERNAL_GET_DESCRIPTOR=1
CFLAGS.rs485hid+= -I$(.OBJDIR) # for public_key.h
SRCS.rs485hid+= usb_hid_base.c

.if empty(HID_PRIV_KEY) && exists($(.CURDIR)/.hid_priv_key)
HID_PRIV_KEY!= cat $(.CURDIR)/.hid_priv_key
.endif

.PHONY: hid_priv_key
hid_priv_key:
@LANG=C tr -c -d a-f0-9 < /dev/urandom | dd bs=1 of=$(.CURDIR)/.hid_priv_key count=64 2>/dev/null
@echo 'Key created and put into .hid_priv_key.'

# make this a phony target so it's always run
# dependancies will only be made when it's updated
.PHONY: $(.OBJDIR)/public_key.h
$(.OBJDIR)/public_key.h:
@if [ "$(HID_PRIV_KEY)" = "" ]; then echo 'Must provide HID_PRIV_KEY make variable or have a non-empty file ".hid_priv_key". This can be created by the command "$(MAKE) hid_priv_key".'; false; fi

@echo 'static const uint8_t pubkey[] = {' $$(python3 -c 'import sys; import codecs; from syote_comms import X25519; print(", ".join(hex(x) for x in X25519.frombytes(codecs.decode(sys.argv[1], "hex")).getpub()))' $(HID_PRIV_KEY) ) "};" > public_key.h.tmp
@echo 'static_assert(sizeof pubkey == 32);' >> public_key.h.tmp

if [ "$$(cat public_key.h.tmp)" = "static uint8_t pubkey[] = { };" -o "$$(cat public_key.h.tmp)" = "" ]; then rm -f "$@"; false; fi

(cmp public_key.h.tmp public_key.h >/dev/null 2>&1 && rm public_key.h.tmp) || mv public_key.h.tmp public_key.h

strobe_pki.o: $(.OBJDIR)/public_key.h

usb_hid_base.c: usb_hid.py
PYTHONPATH=$(.CURDIR) python $(STM32)/usb/usb_gen.py $(.ALLSRC:[1]:T:R)


.PATH: $(.CURDIR)/.. .PATH: $(.CURDIR)/..


CFLAGS += -g CFLAGS += -g
CFLAGS += -I$(.CURDIR)/..


WITH_RS485FRAME = yes
WITH_STM32F103 = yes WITH_STM32F103 = yes
WITH_USB_CDC = yes WITH_USB_CDC = yes
WITH_RS485FRAME = yes
WITH_USB_HID = yes


.include <../mk/boards.mk> .include <../mk/boards.mk>


EXTRA_DEPENDS+= $(STM32)/usb/usb_gen.py

.include <../mk/mu.progs.mk> .include <../mk/mu.progs.mk>

usb_hid_base.c: $(STM32)/usb/usb_gen.py

+ 65
- 0
rs485hid/memdebug.c View File

@@ -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

+ 28
- 8
rs485hid/rs485frame.c View File

@@ -18,7 +18,7 @@


const char *rs485err = NULL; const char *rs485err = NULL;


static uint8_t rxbuf[1024];
static uint8_t rxbuf[128];
static txdone_fn_t txdone; static txdone_fn_t txdone;
static rxdone_fn_t rxdone; static rxdone_fn_t rxdone;
static txdone_fn_t errcb; static txdone_fn_t errcb;
@@ -62,7 +62,7 @@ settx(bool tx)
else else
s = GPIO_PIN_RESET; s = GPIO_PIN_RESET;


HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, s);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, s); /* XXX - led */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, s); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, s);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, s); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, s);
} }
@@ -71,11 +71,19 @@ void
rs485_starttx(uint8_t *pkt, size_t len) rs485_starttx(uint8_t *pkt, size_t len)
{ {


/* XXX - make sure not tx'ing */
/* XXX - make sure not actively rx'ing */
/* XXX - make sure line is idle */ /* XXX - make sure line is idle */
if (rs485uart.RxState == HAL_UART_STATE_BUSY_RX) {
while (rs485uart.RxXferCount != sizeof rxbuf) {
}
}
#if 0
/* XXX - as it's full duplex, this shouldn't be needed */
/* This appears to corrupt state and break things */
if (doingrx) { if (doingrx) {
HAL_UART_AbortReceive_IT(&rs485uart); HAL_UART_AbortReceive_IT(&rs485uart);
} }
#endif


settx(true); settx(true);


@@ -99,6 +107,8 @@ HAL_UART_TxCpltCallback(UART_HandleTypeDef *phuart)
if (phuart != &rs485uart || txdone == NULL) if (phuart != &rs485uart || txdone == NULL)
return; return;


for (int x = 0; x < 1000000; x++);

settx(false); settx(false);
doingtx = 0; doingtx = 0;


@@ -140,12 +150,13 @@ HAL_UART_MspInit(UART_HandleTypeDef *huart)
/* UM1850 § 38.1.2 Page 550 */ /* UM1850 § 38.1.2 Page 550 */


/* 2.a */ /* 2.a */
__HAL_RCC_USART3_FORCE_RESET( );
__HAL_RCC_USART3_RELEASE_RESET( );
__HAL_RCC_USART3_FORCE_RESET();
__HAL_RCC_USART3_RELEASE_RESET();
__HAL_RCC_USART3_CLK_ENABLE(); __HAL_RCC_USART3_CLK_ENABLE();


/* 2.b */ /* 2.b */
__HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE();

/* TX pin */ /* TX pin */
GPIO_InitStruct = (GPIO_InitTypeDef){ GPIO_InitStruct = (GPIO_InitTypeDef){
.Pin = GPIO_PIN_10, .Pin = GPIO_PIN_10,
@@ -197,10 +208,17 @@ rs485_startrx()
return 0; return 0;
} }


doingrx = 1;

r = HAL_UARTEx_ReceiveToIdle_IT(&rs485uart, rxbuf, sizeof rxbuf); r = HAL_UARTEx_ReceiveToIdle_IT(&rs485uart, rxbuf, sizeof rxbuf);
if (r == HAL_BUSY) { if (r == HAL_BUSY) {
rs485err = "already";
return 1;
#if 0
/* This appears to corrupt state and break things */
HAL_UART_AbortReceive_IT(&rs485uart); HAL_UART_AbortReceive_IT(&rs485uart);
r = HAL_UARTEx_ReceiveToIdle_IT(&rs485uart, rxbuf, sizeof rxbuf); r = HAL_UARTEx_ReceiveToIdle_IT(&rs485uart, rxbuf, sizeof rxbuf);
#endif
} }


rs485err = statustostr(r); rs485err = statustostr(r);
@@ -209,14 +227,16 @@ rs485_startrx()
} }


static void static void
rs485frame_init(const void *v)
rs485frame_init(void)
{ {
GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitTypeDef GPIO_InitStruct;


__HAL_RCC_GPIOB_CLK_ENABLE();

/* setup DE/RE */ /* setup DE/RE */
GPIO_InitStruct = (GPIO_InitTypeDef){ GPIO_InitStruct = (GPIO_InitTypeDef){
.Pin = GPIO_PIN_0|GPIO_PIN_1, .Pin = GPIO_PIN_0|GPIO_PIN_1,
.Mode = GPIO_MODE_OUTPUT_OD,
.Mode = GPIO_MODE_OUTPUT_PP,
.Pull = GPIO_NOPULL, .Pull = GPIO_NOPULL,
.Speed = GPIO_SPEED_FREQ_LOW, /* 2 MHz */ .Speed = GPIO_SPEED_FREQ_LOW, /* 2 MHz */
}; };
@@ -240,4 +260,4 @@ rs485frame_init(const void *v)
/* 4 */ /* 4 */
HAL_UART_Init(&rs485uart); HAL_UART_Init(&rs485uart);
} }
SYSINIT(rs485frame_init, SI_SUB_STANDARD, SI_ORDER_FIRST, rs485frame_init, NULL);
SYSINIT_VF(rs485frame_init, SI_SUB_STANDARD, SI_ORDER_FIRST, rs485frame_init);

+ 13
- 60
rs485hid/rs485gw.c View File

@@ -36,7 +36,18 @@


#include <sysinit.h> #include <sysinit.h>


SYSINIT(hal_init, SI_SUB_HAL, SI_ORDER_FIRST, (void (*)(const void *))HAL_Init, NULL);
#include <usb_device.h>
SYSINIT_VF(usb_cdc, SI_SUB_USB, SI_ORDER_MIDDLE, MX_USB_DEVICE_Init);

/* XXX - where's a better place? */
extern PCD_HandleTypeDef hpcd_USB_FS;

void
USB_LP_IRQHandler(void)
{

HAL_PCD_IRQHandler(&hpcd_USB_FS);
}


static int dorx = 0; static int dorx = 0;
static uint8_t rxbuf[128]; static uint8_t rxbuf[128];
@@ -71,51 +82,6 @@ SYSINIT(c13led, SI_SUB_HAL, SI_ORDER_SECOND, c13led, NULL);
#define usb_printf debug_printf #define usb_printf debug_printf
#endif #endif


/*
* Referenced from:
* Projects/STM32F103RB-Nucleo/Applications/USB_Device/HID_Standalone/Src/main.c
*/
static void
oscconfig(const void *none)
{
RCC_ClkInitTypeDef clkinitstruct;
RCC_OscInitTypeDef oscinitstruct;
RCC_PeriphCLKInitTypeDef rccperiphclkinit;

__HAL_RCC_PWR_CLK_ENABLE();

oscinitstruct = (RCC_OscInitTypeDef){
.OscillatorType = RCC_OSCILLATORTYPE_HSE,
.HSEState = RCC_HSE_ON,
.HSEPredivValue = RCC_HSE_PREDIV_DIV1,
.PLL.PLLMUL = RCC_PLL_MUL9,

.PLL.PLLState = RCC_PLL_ON,
.PLL.PLLSource = RCC_PLLSOURCE_HSE,
};

HAL_RCC_OscConfig(&oscinitstruct);

/* USB clock selection */
rccperiphclkinit = (RCC_PeriphCLKInitTypeDef){
.PeriphClockSelection = RCC_PERIPHCLK_USB,
.UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5,
};
HAL_RCCEx_PeriphCLKConfig(&rccperiphclkinit);

/* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
clocks dividers */
clkinitstruct = (RCC_ClkInitTypeDef){
.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2),
.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK,
.AHBCLKDivider = RCC_SYSCLK_DIV1,
.APB1CLKDivider = RCC_HCLK_DIV2,
.APB2CLKDivider = RCC_HCLK_DIV1,
};
HAL_RCC_ClockConfig(&clkinitstruct, FLASH_LATENCY_2);
}
SYSINIT(oscconfig, SI_SUB_HAL, SI_ORDER_THIRD, oscconfig, NULL);

char * char *
findeol(char *pos, size_t len) findeol(char *pos, size_t len)
{ {
@@ -167,19 +133,6 @@ rxdone(const uint8_t *payload, size_t size)
gotrxdone = 1; gotrxdone = 1;
} }


void
rxtimeout(void)
{

usb_printf("rxtimeout\r\n");
}

void
rxerr(void)
{
usb_printf("rxerr\r\n");
}

static uint8_t static uint8_t
hexchartonib(char s) hexchartonib(char s)
{ {
@@ -278,7 +231,7 @@ main(void)


debug_printf("starting...\n"); debug_printf("starting...\n");


#if 1
#if 0
int i; int i;
for (i = 0; i < 5; i++) { for (i = 0; i < 5; i++) {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);


+ 558
- 0
rs485hid/rs485hid.c View File

@@ -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;
}

+ 87
- 0
rs485hid/strobe_pki.c View File

@@ -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;
}

+ 9
- 0
rs485hid/strobe_pki.h View File

@@ -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]);

+ 62
- 0
rs485hid/usb_hid.py View File

@@ -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

+ 328
- 0
rs485hid/usb_hid_def.c View File

@@ -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;
}

+ 6
- 0
rs485hid/usb_hid_def.h View File

@@ -0,0 +1,6 @@
#include <stdint.h>

#define REPORT_SIZE 8

void report_process(void);
int report_insert(uint8_t rep[REPORT_SIZE]);

+ 6
- 17
si_usb.c View File

@@ -1,16 +1,18 @@
/* /*
* Code for initalizing USB CDC via the SYSINIT mechanism.
* Code for reseting USB via the SYSINIT mechanism.
*
* Note: This works for both Node151 and BluePill (F1) as PA12 is
* USB_DP on both.
*/ */


#include <sysinit.h> #include <sysinit.h>


#include <stm32f1xx_hal_gpio.h> #include <stm32f1xx_hal_gpio.h>
#include <stm32f1xx_hal_pcd.h>


#include <usb_device.h> #include <usb_device.h>


static void static void
usb_cdc_init(const void *foo)
usb_reset(void)
{ {
/* /*
* pretend that the device was newly plugged in * pretend that the device was newly plugged in
@@ -32,18 +34,5 @@ usb_cdc_init(const void *foo)
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET);


HAL_Delay(10); HAL_Delay(10);

MX_USB_DEVICE_Init();
}

extern PCD_HandleTypeDef hpcd_USB_FS;


void
USB_LP_IRQHandler(void)
{

HAL_PCD_IRQHandler(&hpcd_USB_FS);
} }

SYSINIT(usb_cdc_init, SI_SUB_CONSOLE, SI_ORDER_FIRST, usb_cdc_init, NULL);
SYSINIT_VF(usb_reset, SI_SUB_USB, SI_ORDER_FIRST, usb_reset);

+ 16
- 1
stm32/f103c8t6/STM32F103C8T6_FLASH.ld View File

@@ -55,6 +55,8 @@ _estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */
_Min_Heap_Size = 0x200 ; /* required amount of heap */ _Min_Heap_Size = 0x200 ; /* required amount of heap */
_Min_Stack_Size = 0x400 ; /* required amount of stack */ _Min_Stack_Size = 0x400 ; /* required amount of stack */


_flash_page_size = 0x400 ; /* size of a flash page */

/* Memories definition */ /* Memories definition */
MEMORY MEMORY
{ {
@@ -100,7 +102,6 @@ SECTIONS
__start_set_sysinit_set = .; __start_set_sysinit_set = .;
KEEP(*(set_sysinit_set)) /* sysinit linker sets */ KEEP(*(set_sysinit_set)) /* sysinit linker sets */
__stop_set_sysinit_set = .; __stop_set_sysinit_set = .;
. = ALIGN(4);
} >FLASH } >FLASH


.ARM.extab : { .ARM.extab : {
@@ -162,6 +163,20 @@ SECTIONS
} >RAM AT> FLASH } >RAM AT> FLASH


.savestate :
{
. = ALIGN(_flash_page_size);
*(.savestate) /* save state */
. = ALIGN(_flash_page_size);
} >FLASH

.savekeys :
{
. = ALIGN(_flash_page_size);
*(.savekeys) /* save keys */
. = ALIGN(_flash_page_size);
} >FLASH

/* Uninitialized data section into "RAM" Ram type memory */ /* Uninitialized data section into "RAM" Ram type memory */
. = ALIGN(4); . = ALIGN(4);
.bss : .bss :


+ 216
- 0
stm32/usb/usb_gen.py View File

@@ -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')

+ 853
- 0
stm32/usb/usbd_ctlreq_nodesc.c View File

@@ -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>&copy; 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****/

+ 4
- 4
strobe_rng_init.c View File

@@ -31,8 +31,6 @@
#include <stm32l1xx.h> #include <stm32l1xx.h>
#include <stm32l1xx_hal_flash_ex.h> #include <stm32l1xx_hal_flash_ex.h>


#include <utilities.h>

#include <unistd.h> #include <unistd.h>


#define nitems(x) (sizeof(x) / sizeof *(x)) #define nitems(x) (sizeof(x) / sizeof *(x))
@@ -66,6 +64,7 @@ strobe_rng_save(void)
{ {
rng_word_t tmp[sizeof rng_save / sizeof(rng_word_t)]; rng_word_t tmp[sizeof rng_save / sizeof(rng_word_t)];
size_t i; size_t i;
uint32_t primask;
int r; int r;


/* /*
@@ -74,7 +73,8 @@ strobe_rng_save(void)
r = strobe_randomize((uint8_t *)tmp, sizeof tmp); r = strobe_randomize((uint8_t *)tmp, sizeof tmp);
(void)r; (void)r;


CRITICAL_SECTION_BEGIN();
primask = __get_PRIMASK();
__disable_irq( );


HAL_FLASHEx_DATAEEPROM_Unlock(); HAL_FLASHEx_DATAEEPROM_Unlock();


@@ -83,5 +83,5 @@ strobe_rng_save(void)


HAL_FLASHEx_DATAEEPROM_Lock(); HAL_FLASHEx_DATAEEPROM_Lock();


CRITICAL_SECTION_END();
__set_PRIMASK(primask);
} }

+ 0
- 4
strobe_rng_init.h View File

@@ -3,10 +3,6 @@
#define roundup(x, y) ((((x) + (y) - 1) / (y)) * (y)) #define roundup(x, y) ((((x) + (y) - 1) / (y)) * (y))


typedef uint32_t rng_word_t; typedef uint32_t rng_word_t;
extern const rng_word_t rng_save[roundup(32, sizeof(rng_word_t)) / sizeof(rng_word_t)];

void strobe_rng_init(void);
void strobe_rng_save(void);


static inline void static inline void
bare_strobe_randomize(uint8_t *ptr, ssize_t len) bare_strobe_randomize(uint8_t *ptr, ssize_t len)


Loading…
Cancel
Save