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