Compare commits

...

29 Commits

Author SHA1 Message Date
26af0c423e Update solo-extras.md 2019-08-16 14:04:43 +08:00
19422d9daa add info for rng use 2019-08-16 14:04:43 +08:00
b7a4cf001a run through fixes 2019-08-16 14:04:43 +08:00
3927aec06d dont remove solo.hex bootloader.hex 2019-08-16 14:04:43 +08:00
f5794481ae initial draft 2019-08-16 14:04:43 +08:00
caac9d0cc1 add secure build that uses default attestation key 2019-08-16 14:04:43 +08:00
2423154fee fix warning 2019-08-15 18:07:40 +08:00
cf79b7865d small fix 2019-08-15 17:50:16 +08:00
6f0cf99c92 PPS implementation 2019-08-15 17:50:16 +08:00
7ef68fd5d3 Merge pull request #265 from solokeys/fix_cdc_interfaces
Fix cdc interfaces
2019-08-15 17:49:53 +08:00
3be8611fcf remove duplicate from merge 2019-08-15 17:44:09 +08:00
21489658a7 Merge branch 'master' into fix_cdc_interfaces 2019-08-15 17:38:57 +08:00
a07a3dee8d refactor user_presence handling 2019-08-15 17:36:35 +08:00
416da63a9a not for bootloader 2019-08-15 17:36:35 +08:00
027fa791a3 only 1 user presence auth per button press 2019-08-15 17:36:35 +08:00
3e52d7b42b cache button press for 2s 2019-08-15 17:36:35 +08:00
301e18c6a2 add some int0 logic to main cycle 2019-08-14 14:32:03 +08:00
44205141eb add one place for int0 2019-08-14 14:32:03 +08:00
6e1110ca9b fix bug with ams_wait_for_tx 2019-08-14 14:32:03 +08:00
9105b988e2 fix some warnings 2019-08-14 14:32:03 +08:00
14c94ea8f5 minor typo 2019-08-14 14:26:45 +08:00
435b908c17 Merge pull request #241 from Wesseldr/feature/STM32L432_documentation_update
Added OsX arm install, updated FIDO2 test site links
2019-08-14 14:23:41 +08:00
78280e570b adjust whitespace 2019-08-12 16:18:47 +08:00
36aec9f20b separate interface into two and add "IAD" descriptor 2019-08-12 16:18:30 +08:00
b5d3276df6 not for bootloader 2019-08-11 18:16:58 +08:00
ffd854a303 only 1 user presence auth per button press 2019-08-11 18:05:08 +08:00
349a84dcec cache button press for 2s 2019-08-11 17:59:31 +08:00
6c6a9bc5b6 Merge pull request #260 from solokeys/all-contributors/add-szszszsz
docs: add szszszsz as a contributor
2019-08-08 01:14:36 +02:00
02a51454b7 Added OsX arm install, updated FIDO2 test site links 2019-08-07 18:35:41 +02:00
16 changed files with 2322 additions and 311 deletions

View File

@ -0,0 +1,51 @@
# Booting into bootloader mode
You can put Solo into bootloader mode by holding down the button, and plugging in Solo. After 2 seconds, bootloader mode will activate.
You'll see a yellowish flashing light and you can let go of the button.
Now Solo is ready to [accept firmware updates](/solo/signed-updates). If the Solo is a secured model, it can only accept signed updates, typically in the `firmware-*.json` format.
If Solo is running a hacker build, it can be put into bootloader mode on command. This makes it easier for development.
```bash
solo program aux enter-bootloader
```
# The boot stages of Solo
Solo has 3 boot stages.
## DFU
The first stage is the DFU (Device Firmware Update) which is in a ROM on Solo. It is baked into the chip and is not implemented by us.
This is what allows the entire firmware of Solo to be programmed. **It's not recommended to develop for Solo using the DFU because
if you program broken firmware, you could brick your device**.
On hacker devices, you can boot into the DFU by holding down the button for 5 seconds, when Solo is already in bootloader mode.
You can also run this command when Solo is in bootloader mode to put it in DFU mode.
```bash
solo program aux enter-dfu
```
Note it will stay in DFU mode until to tell it to boot again. You can boot it again by running the following.
```bash
solo program aux leave-dfu
```
*Warning*: If you change the firmware to something broken, and you tell the DFU to boot it, you could brick your device.
## Solo Bootloader
The next boot stage is the "Solo bootloader". So when we say to put your Solo into bootloader mode, it is this stage.
This bootloader is written by us and allows signed firmware updates to be written. On Solo Hackers, there is no signature checking
and will allow any firmware updates.
It is safe to develop for Solo using our Solo bootloader. If broken firmware is uploaded to the device, then the Solo
bootloader can always be booted again by holding down the button when plugging in.
## Solo application
This is what contains all the important functionality of Solo. FIDO2, U2F, etc. This is what Solo will boot to by default.

View File

@ -14,12 +14,6 @@ but be warned they might be out of date. Typically it will be called `gcc-arm-n
Install `solo-python` usually with `pip3 install solo-python`. The `solo` python application may also be used for [programming](#programming). Install `solo-python` usually with `pip3 install solo-python`. The `solo` python application may also be used for [programming](#programming).
To program your build, you'll need one of the following programs.
- [openocd](http://openocd.org)
- [stlink](https://github.com/texane/stlink)
- [STM32CubeProg](https://www.st.com/en/development-tools/stm32cubeprog.html)
## Obtain source code and solo tool ## Obtain source code and solo tool
Source code can be downloaded from: Source code can be downloaded from:
@ -32,7 +26,7 @@ Source code can be downloaded from:
- from python programs [repository](https://pypi.org/project/solo-python/) `pip install solo-python` - from python programs [repository](https://pypi.org/project/solo-python/) `pip install solo-python`
- from installing prerequisites `pip3 install -r tools/requirements.txt` - from installing prerequisites `pip3 install -r tools/requirements.txt`
- github repository: [repository](https://github.com/solokeys/solo-python) - github repository: [repository](https://github.com/solokeys/solo-python)
- installation python enviroment witn command `make venv` from root directory of source code - installation python enviroment with command `make venv` from root directory of source code
## Compilation ## Compilation
@ -54,7 +48,7 @@ enabled, like being able to jump to the bootloader on command. It then merges b
and solo builds into the same binary. I.e. it combines `bootloader.hex` and `solo.hex` and solo builds into the same binary. I.e. it combines `bootloader.hex` and `solo.hex`
into `all.hex`. into `all.hex`.
If you're just planning to do development, please don't try to reprogram the bootloader, If you're just planning to do development, **please don't try to reprogram the bootloader**,
as this can be risky if done often. Just use `solo.hex`. as this can be risky if done often. Just use `solo.hex`.
### Building with debug messages ### Building with debug messages
@ -86,6 +80,8 @@ solo monitor <serial-port>
### Building a Solo release ### Building a Solo release
To build Solo
If you want to build a release of Solo, we recommend trying a Hacker build first If you want to build a release of Solo, we recommend trying a Hacker build first
just to make sure that it's working. Otherwise it may not be as easy or possible to just to make sure that it's working. Otherwise it may not be as easy or possible to
fix any mistakes. fix any mistakes.
@ -96,105 +92,13 @@ If you're ready to program a full release, run this recipe to build.
make build-release-locked make build-release-locked
``` ```
Programming `all.hex` will cause the device to permanently lock itself. This outputs bootloader.hex, solo.hex, and the combined all.hex.
## Programming Programming `all.hex` will cause the device to permanently lock itself. This means debuggers cannot be used and signature checking
will be enforced on all future updates.
It's recommended to test a debug/hacker build first to make sure Solo is working as expected. Note if you program a secured `solo.hex` file onto a Solo Hacker, it will lock the flash, but the bootloader
Then you can switch to a locked down build, which cannot be reprogrammed as easily (or not at all!). will still accept unsigned firmware updates. So you can switch it back to being a hacker, but you will
not be able to replace the unlocked bootloader anymore, since the permanently locked flash also disables the DFU.
[Read more on Solo's boot stages](/solo/bootloader-mode).
We recommend using our `solo` tool to manage programming. It is cross platform. First you must
install the prerequisites:
```
pip3 install -r tools/requirements.txt
```
If you're on Windows, you must also install [libusb](https://sourceforge.net/projects/libusb-win32/files/libusb-win32-releases/1.2.6.0/).
### Pre-programmed Solo Hacker
If your Solo device is already programmed (it flashes green when powered), we recommend
programming it using the Solo bootloader.
```
solo program aux enter-bootloader
solo program bootloader solo.hex
```
Make sure to program `solo.hex` and not `all.hex`. Nothing bad would happen, but you'd
see errors.
If something bad happens, you can always boot the Solo bootloader by doing the following.
1. Unplug device.
2. Hold down button.
3. Plug in device while holding down button.
4. Wait about 2 seconds for flashing yellow light. Release button.
If you hold the button for an additional 5 seconds, it will boot to the ST DFU (device firmware update).
Don't use the ST DFU unless you know what you're doing.
### ST USB DFU
If your Solo has never been programmed, it will boot the ST USB DFU. The LED is turned
off and it enumerates as "STM BOOTLOADER".
You can program it by running the following.
```
solo program aux enter-bootloader
solo program aux enter-dfu
# powercycle key
solo program dfu all.hex
```
Make sure to program `all.hex`, as this contains both the bootloader and the Solo application.
If all goes well, you should see a slow-flashing green light.
### Solo Hacker vs Solo
A Solo hacker device doesn't need to be in bootloader mode to be programmed, it will automatically switch.
Solo (locked) needs the button to be held down when plugged in to boot to the bootloader.
A locked Solo will only accept signed updates.
### Signed updates
If this is not a device with a hacker build, you can only program signed updates.
```
solo program bootloader /path/to/firmware.json
```
If you've provisioned the Solo bootloader with your own secp256r1 public key, you can sign your
firmware by running the following command.
```
solo sign /path/to/signing-key.pem /path/to/solo.hex /output-path/to/firmware.json
```
If your Solo isn't locked, you can always reprogram it using a debugger connected directly
to the token.
## Permanently locking the device
If you plan to be using your Solo for real, you should lock it permanently. This prevents
someone from connecting a debugger to your token and stealing credentials.
To do this, build the locked release firmware.
```
make build-release-locked
```
Now when you program `all.hex`, the device will lock itself when it first boots. You can only update it
with signed updates.
If you'd like to also permanently disable signed updates, plug in your programmed Solo and run the following:
```
# WARNING: No more signed updates.
solo program disable-bootloader
```

141
docs/solo/customization.md Normal file
View File

@ -0,0 +1,141 @@
# Customization
If you are interested in customizing parts of your Solo, and you have a Solo Hacker, this page is for you.
## Custom Attestation key
The attestation key is used in the FIDO2 *makeCredential* or U2F *register* requests. It signs
newly generated credentials. The certificate associated with the attestation key is output with newly created credentials.
Platforms or services can use the attestation feature to enforce specific authenticators to be used.
This is typically a use case for organizations and isn't seen in the wild for consumer use cases.
Attestation keys are typically the same for at least 100K units of a particular authenticator model.
This is so they don't contribute a significant fingerprint that platforms could use to identify the user.
If you don't want to use the default attestation key that Solo builds with, you can create your own
and program it.
### Creating your attestation key pair
Since we are generating keys, it's important to use a good entropy source.
You can use the [True RNG on your Solo](/solo/solo-extras) to generate some good random numbers.
```
# Run for 1 second, then hit control-c
solo key rng raw > seed.bin
```
First we will create a self signed key pair that acts as the root of trust. This
won't go on the authenticator, but will sign the keypair that does.
Please change the root certification information as needed. You may change the ECC curve.
```
curve=prime256v1
country=US
state=Maine
organization=OpenSourceSecurity
unit="Root CA"
CN=example.com
email=example@example.com
# generate EC private key
openssl ecparam -genkey -name "$curve" -out root_key.pem -rand seed.bin
# generate a "signing request"
openssl req -new -key root_key.pem -out root_key.pem.csr -subj "/C=$country/ST=$state/O=$organization/OU=$unit/CN=example.com/emailAddress=$email"
# self sign the request
openssl x509 -trustout -req -days 18250 -in root_key.pem.csr -signkey root_key.pem -out root_cert.pem -sha256
# convert to smaller size format DER
openssl x509 -in root_cert.pem -outform der -out root_cert.der
# print out information and verify
openssl x509 -in root_cert.pem -text -noout
```
You need to create a extended certificate for the device certificate to work with FIDO2. You need to create this
file, `v3.ext`, and add these options to it.
```
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
```
Now to generate & sign the attestation key pair that will go on your device, or maybe 100,000 devices :).
Note you must use a prime256v1 curve for this step, and you must leave the unit/OU as "Authenticator Attestation".
```
country=US
state=Maine
organization=OpenSourceSecurity
unit="Authenticator Attestation"
CN=example.com
email=example@example.com
# generate EC private key
openssl ecparam -genkey -name "$curve" -out device_key.pem -rand seed.bin
# generate a "signing request"
openssl req -new -key device_key.pem -out device_key.pem.csr -subj "/C=$country/ST=$state/O=$organization/OU=$unit/CN=example.com/emailAddress=$email"
# sign the request
openssl x509 -req -days 18250 -in device_key.pem.csr -extfile v3.ext -CA root_cert.pem -CAkey root_key.pem -set_serial 01 -out device_cert.pem -sha256
# convert to smaller size format DER
openssl x509 -in device_cert.pem -outform der -out device_cert.der
# Verify the device certificate details
openssl x509 -in device_cert.pem -text -noout
```
Let's verify that the attestation key and certificate are valid, and that they can be verified with the root key pair.
```
echo 'challenge $RANDOM' > chal.txt
# check that they are valid key pairs
openssl dgst -sha256 -sign device_key.pem -out sig.txt chal.txt
openssl dgst -sha256 -verify <(openssl x509 -in device_cert.pem -pubkey -noout) -signature sig.txt chal.txt
openssl dgst -sha256 -sign "root_key.pem" -out sig.txt chal.txt
openssl dgst -sha256 -verify <(openssl x509 -in root_cert.pem -pubkey -noout) -signature sig.txt chal.txt
# Check they are a chain
openssl verify -verbose -CAfile "root_cert.pem" "device_cert.pem"
```
If the checks succeed, you are ready to program the device attestation key and certificate.
### Programming an attestation key and certificate
Convert the DER format of the device attestation certificate to "C" bytes using our utility script. You may first need to
first install prerequisite python modules (pip install -r tools/requirements.txt).
```
python tools/gencert/cbytes.py device_cert.der
```
Copy the byte string portion into the [`attestation.c` source file of Solo](https://github.com/solokeys/solo/blob/master/targets/stm32l432/src/attestation.c). Overwrite the development or "default" certificate that is already there.
Now [build the Solo firmware](/solo/building), either a secure or hacker build. You will need to produce a bootloader.hex file and a solo.hex file.
Print your attestation key in a hex string format.
```
python tools/print_x_y.py device_key.pem
```
Merge the bootloader.hex, solo.hex, and attestion key into one firmware file.
```
solo mergehex --attestation-key <attestation-key-hex-string> bootloader.hex solo.hex all.hex
```
Now you have a newly create `all.hex` file with a custom attestation key. You can [program this all.hex file
with Solo in DFU mode](/solo/programming#procedure).

View File

@ -66,7 +66,7 @@ Environment: Fedora 29 x64, Linux 4.19.9
See <https://docs.solokeys.io/solo/building/> for the original guide. Here details not included there will be covered. See <https://docs.solokeys.io/solo/building/> for the original guide. Here details not included there will be covered.
### Install ARM tools ### Install ARM tools Linux
1. Download current [ARM tools] package: [gcc-arm-none-eabi-8-2018-q4-major-linux.tar.bz2]. 1. Download current [ARM tools] package: [gcc-arm-none-eabi-8-2018-q4-major-linux.tar.bz2].
@ -75,6 +75,13 @@ See <https://docs.solokeys.io/solo/building/> for the original guide. Here detai
3. Add full path to the `./bin` directory as first entry to the `$PATH` variable, 3. Add full path to the `./bin` directory as first entry to the `$PATH` variable,
as in `~/gcc-arm/gcc-arm-none-eabi-8-2018-q4-major/bin/:$PATH`. as in `~/gcc-arm/gcc-arm-none-eabi-8-2018-q4-major/bin/:$PATH`.
### Install ARM tools OsX using brew package manager
```bash
brew tap ArmMbed/homebrew-formulae
brew install arm-none-eabi-gcc
```
### Install flashing software ### Install flashing software
ST provides a CLI flashing tool - `STM32_Programmer_CLI`. It can be downloaded directly from the vendor's site: ST provides a CLI flashing tool - `STM32_Programmer_CLI`. It can be downloaded directly from the vendor's site:
@ -114,8 +121,8 @@ Do not use it, if you do not plan to do so.
```bash ```bash
# while in the main project directory # while in the main project directory
# create Python virtual environment with required packages, and activate # create Python virtual environment with required packages, and activate
make env3 make venv
. env3/bin/activate . venv/bin/activate
# Run flashing # Run flashing
cd ./targets/stm32l432 cd ./targets/stm32l432
make flash make flash
@ -178,8 +185,8 @@ make fido2-test
#### FIDO2 test sites #### FIDO2 test sites
1. <https://webauthn.bin.coffee/> 1. <https://www.passwordless.dev/overview>
2. <https://github.com/apowers313/fido2-server-demo/> 2. <https://webauthn.bin.coffee/>
3. <https://webauthn.org/> 3. <https://webauthn.org/>
#### U2F test sites #### U2F test sites

113
docs/solo/programming.md Normal file
View File

@ -0,0 +1,113 @@
# Programming
This page documents how to update or program your Solo.
## Prerequisites
To program Solo, you'll likely only need to use our Solo tool.
```python
pip3 install solo-python
```
## Updating the firmware
If you just want to update the firmware, you can run one of the following commands.
Make sure your key [is in bootloader mode](/solo/bootloader-mode#solo-bootloader) first.
```bash
solo key update <--secure | --hacker>
```
You can manually install the [latest release](https://github.com/solokeys/solo/releases), or use a build that you made.
```bash
# If it's a hacker, it will automatically boot into bootloader mode.
solo program bootloader <firmware.hex | firmware.json>
```
Note you won't be able to use `all.hex` or the `bundle-*.hex` builds, as these include the solo bootloader. You shouldn't
risk changing the Solo bootloader unless you want to make it a secure device, or [make other customizations]().
## Updating a Hacker to a Secure Solo
Updating a hacker to be a secure build overwrites the [Solo bootloader](/solo/bootloader-mode#solo-bootloader).
So it's important to not mess this up or you may brick your device.
You can use a firmware build from the [latest release](https://github.com/solokeys/solo/releases) or use
a build that you made yourself.
You need to use a firmware file that has the combined bootloader and application (or at the very least just the bootloader).
This means using the `bundle-*.hex` file or the `all.hex` from your build. If you overwrite the Solo flash with a missing bootloader,
it will be bricked.
We provide two types of bundled builds. The `bundle-hacker-*.hex` build is the hacker build. If you update with this,
you will update the bootloader and application, but nothing will be secured. The `bundle-secure-non-solokeys.hex`
is a secured build that will lock your device and it will behave just like a Secure Solo. The main difference is that
it uses a "default" attestation key in the device, rather than the SoloKeys attestation key. There is no security
concern with using our default attestation key, aside from a privacy implication that services can distinguish it from Solo Secure.
### Procedure
1. Boot into DFU mode.
# Enter Solo bootloader
solo program aux enter-bootloader
# Enter DFU
solo program aux enter-dfu
The device should be turned off.
2. Program the device
solo program dfu <bundle-secure-non-solokeys.hex | all.hex>
Double check you programmed it with bootloader + application (or just bootloader).
If you messed it up, simply don't do the next step and repeat this step correctly.
3. Boot the device
Once Solo boots a secure build, it will lock the flash permantly from debugger access. Also the bootloader
will only accept signed firmware updates.
solo program aux leave-dfu
If you are having problems with solo tool and DFU mode, you could alternatively try booting into DFU
by holding down the button while Solo is in bootloader mode. Then try another programming tool that works
with ST DFU:
* STM32CubeProg
* openocd
* stlink
Windows users need to install [libusb](https://sourceforge.net/projects/libusb-win32/files/libusb-win32-releases/1.2.6.0/)
for solo-python to work with Solo's DFU.
## Programming a Solo that hasn't been programmed
A Solo that hasn't been programmed will boot into DFU mode. You can program
it by following a bootloader, or combined bootloader + application.
```
solo program dfu <bundle-*.hex | all.hex>
```
Then boot the device. Make sure it has a bootloader to boot to.
```
solo program aux leave-dfu
```
## Disable signed firmware updates
If you'd like to also permanently disable signed updates, plug in your programmed Solo and run the following:
```bash
# WARNING: No more signed updates.
solo program disable-bootloader
```
You won't be able to update to any new releases.

19
docs/solo/solo-extras.md Normal file
View File

@ -0,0 +1,19 @@
# Solo Extras
## Random number generation
Solo contains a True Random Number Generator (TRNG). A TRNG is a hardware based mechanism
that leverages natural phenomenon to generate random numbers, which is can be better than a traditional
RNG that has state and updates deterministically using cryptographic methods.
You can easily access the TRNG stream on Solo using our python tool [solo-python](https://github.com/solokeys/solo-python).
```
solo key rng raw > random.bin
```
Or you can seed the state of the RNG on your kernel (/dev/random).
```
solo key rng feedkernel
```

View File

@ -38,6 +38,7 @@ build firmware hacker solo
build firmware hacker-debug-1 solo build firmware hacker-debug-1 solo
build firmware hacker-debug-2 solo build firmware hacker-debug-2 solo
build firmware secure solo build firmware secure solo
build firmware secure-non-solokeys solo
pip install -U pip pip install -U pip
pip install -U solo-python pip install -U solo-python
@ -49,3 +50,6 @@ bundle="bundle-hacker-debug-1-${version}"
/opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-hacker-debug-1-${version}.hex ${bundle}.hex /opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-hacker-debug-1-${version}.hex ${bundle}.hex
bundle="bundle-hacker-debug-2-${version}" bundle="bundle-hacker-debug-2-${version}"
/opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-hacker-debug-2-${version}.hex ${bundle}.hex /opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-hacker-debug-2-${version}.hex ${bundle}.hex
bundle="bundle-secure-non-solokeys-${version}"
/opt/conda/bin/solo mergehex bootloader-verifying-${version}.hex firmware-secure-non-solokeys-${version}.hex ${bundle}.hex
sha256sum ${bundle}.hex > ${bundle}.sha2

View File

@ -11,6 +11,10 @@ nav:
- FIDO2 Implementation: solo/fido2-impl.md - FIDO2 Implementation: solo/fido2-impl.md
- Metadata Statements: solo/metadata-statements.md - Metadata Statements: solo/metadata-statements.md
- Build instructions: solo/building.md - Build instructions: solo/building.md
- Programming instructions: solo/programming.md
- Bootloader mode: solo/bootloader-mode.md
- Customization: solo/customization.md
- Solo Extras: solo/solo-extras.md
- Running on Nucleo32 board: solo/nucleo32-board.md - Running on Nucleo32 board: solo/nucleo32-board.md
- Signed update process: solo/signed-updates.md - Signed update process: solo/signed-updates.md
- Code documentation: solo/code-overview.md - Code documentation: solo/code-overview.md

View File

@ -21,6 +21,9 @@ firmware-hacker-debug-1:
firmware-hacker-debug-2: firmware-hacker-debug-2:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=2 EXTRA_DEFINES='-DSOLO_HACKER -DFLASH_ROP=0' $(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=2 EXTRA_DEFINES='-DSOLO_HACKER -DFLASH_ROP=0'
firmware-secure-non-solokeys:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0 EXTRA_DEFINES='-DFLASH_ROP=2'
firmware-secure: firmware-secure:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0 EXTRA_DEFINES='-DUSE_SOLOKEYS_CERT -DFLASH_ROP=2' $(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0 EXTRA_DEFINES='-DUSE_SOLOKEYS_CERT -DFLASH_ROP=2'
@ -59,7 +62,6 @@ boot-no-sig:
build-release-locked: cbor clean2 boot-sig-checking clean all-locked build-release-locked: cbor clean2 boot-sig-checking clean all-locked
$(VENV) $(merge_hex) solo.hex bootloader.hex all.hex $(VENV) $(merge_hex) solo.hex bootloader.hex all.hex
rm -f solo.hex bootloader.hex # don't program solo.hex ...
build-release: cbor clean2 boot-sig-checking clean all build-release: cbor clean2 boot-sig-checking clean all
$(VENV) $(merge_hex) solo.hex bootloader.hex all.hex $(VENV) $(merge_hex) solo.hex bootloader.hex all.hex

View File

@ -6,7 +6,7 @@ AR=$(PREFIX)arm-none-eabi-ar
DRIVER_LIBS := lib/stm32l4xx_hal_pcd.c lib/stm32l4xx_hal_pcd_ex.c lib/stm32l4xx_ll_gpio.c \ DRIVER_LIBS := lib/stm32l4xx_hal_pcd.c lib/stm32l4xx_hal_pcd_ex.c lib/stm32l4xx_ll_gpio.c \
lib/stm32l4xx_ll_rcc.c lib/stm32l4xx_ll_rng.c lib/stm32l4xx_ll_tim.c \ lib/stm32l4xx_ll_rcc.c lib/stm32l4xx_ll_rng.c lib/stm32l4xx_ll_tim.c \
lib/stm32l4xx_ll_usb.c lib/stm32l4xx_ll_utils.c lib/stm32l4xx_ll_pwr.c \ lib/stm32l4xx_ll_usb.c lib/stm32l4xx_ll_utils.c lib/stm32l4xx_ll_pwr.c \
lib/stm32l4xx_ll_usart.c lib/stm32l4xx_ll_spi.c lib/stm32l4xx_ll_usart.c lib/stm32l4xx_ll_spi.c lib/stm32l4xx_ll_exti.c
USB_LIB := lib/usbd/usbd_cdc.c lib/usbd/usbd_cdc_if.c lib/usbd/usbd_composite.c \ USB_LIB := lib/usbd/usbd_cdc.c lib/usbd/usbd_cdc_if.c lib/usbd/usbd_composite.c \
lib/usbd/usbd_conf.c lib/usbd/usbd_core.c lib/usbd/usbd_ioreq.c \ lib/usbd/usbd_conf.c lib/usbd/usbd_core.c lib/usbd/usbd_ioreq.c \

View File

@ -0,0 +1,290 @@
/**
******************************************************************************
* @file stm32l4xx_ll_exti.c
* @author MCD Application Team
* @brief EXTI LL module driver.
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2017 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
#if defined(USE_FULL_LL_DRIVER)
/* Includes ------------------------------------------------------------------*/
#include "stm32l4xx_ll_exti.h"
#ifdef USE_FULL_ASSERT
#include "stm32_assert.h"
#else
#define assert_param(expr) ((void)0U)
#endif
/** @addtogroup STM32L4xx_LL_Driver
* @{
*/
#if defined (EXTI)
/** @defgroup EXTI_LL EXTI
* @{
*/
/* Private types -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private constants ---------------------------------------------------------*/
/* Private macros ------------------------------------------------------------*/
/** @addtogroup EXTI_LL_Private_Macros
* @{
*/
#define IS_LL_EXTI_LINE_0_31(__VALUE__) (((__VALUE__) & ~LL_EXTI_LINE_ALL_0_31) == 0x00000000U)
#define IS_LL_EXTI_LINE_32_63(__VALUE__) (((__VALUE__) & ~LL_EXTI_LINE_ALL_32_63) == 0x00000000U)
#define IS_LL_EXTI_MODE(__VALUE__) (((__VALUE__) == LL_EXTI_MODE_IT) \
|| ((__VALUE__) == LL_EXTI_MODE_EVENT) \
|| ((__VALUE__) == LL_EXTI_MODE_IT_EVENT))
#define IS_LL_EXTI_TRIGGER(__VALUE__) (((__VALUE__) == LL_EXTI_TRIGGER_NONE) \
|| ((__VALUE__) == LL_EXTI_TRIGGER_RISING) \
|| ((__VALUE__) == LL_EXTI_TRIGGER_FALLING) \
|| ((__VALUE__) == LL_EXTI_TRIGGER_RISING_FALLING))
/**
* @}
*/
/* Private function prototypes -----------------------------------------------*/
/* Exported functions --------------------------------------------------------*/
/** @addtogroup EXTI_LL_Exported_Functions
* @{
*/
/** @addtogroup EXTI_LL_EF_Init
* @{
*/
/**
* @brief De-initialize the EXTI registers to their default reset values.
* @retval An ErrorStatus enumeration value:
* - 0x00: EXTI registers are de-initialized
*/
uint32_t LL_EXTI_DeInit(void)
{
/* Interrupt mask register set to default reset values */
LL_EXTI_WriteReg(IMR1, 0xFF820000U);
/* Event mask register set to default reset values */
LL_EXTI_WriteReg(EMR1, 0x00000000U);
/* Rising Trigger selection register set to default reset values */
LL_EXTI_WriteReg(RTSR1, 0x00000000U);
/* Falling Trigger selection register set to default reset values */
LL_EXTI_WriteReg(FTSR1, 0x00000000U);
/* Software interrupt event register set to default reset values */
LL_EXTI_WriteReg(SWIER1, 0x00000000U);
/* Pending register clear */
LL_EXTI_WriteReg(PR1, 0x007DFFFFU);
/* Interrupt mask register 2 set to default reset values */
#if defined(LL_EXTI_LINE_40)
LL_EXTI_WriteReg(IMR2, 0x00000187U);
#else
LL_EXTI_WriteReg(IMR2, 0x00000087U);
#endif
/* Event mask register 2 set to default reset values */
LL_EXTI_WriteReg(EMR2, 0x00000000U);
/* Rising Trigger selection register 2 set to default reset values */
LL_EXTI_WriteReg(RTSR2, 0x00000000U);
/* Falling Trigger selection register 2 set to default reset values */
LL_EXTI_WriteReg(FTSR2, 0x00000000U);
/* Software interrupt event register 2 set to default reset values */
LL_EXTI_WriteReg(SWIER2, 0x00000000U);
/* Pending register 2 clear */
LL_EXTI_WriteReg(PR2, 0x00000078U);
return 0x00u;
}
/**
* @brief Initialize the EXTI registers according to the specified parameters in EXTI_InitStruct.
* @param EXTI_InitStruct pointer to a @ref LL_EXTI_InitTypeDef structure.
* @retval An ErrorStatus enumeration value:
* - 0x00: EXTI registers are initialized
* - any other calue : wrong configuration
*/
uint32_t LL_EXTI_Init(LL_EXTI_InitTypeDef *EXTI_InitStruct)
{
uint32_t status = 0x00u;
/* Check the parameters */
assert_param(IS_LL_EXTI_LINE_0_31(EXTI_InitStruct->Line_0_31));
assert_param(IS_LL_EXTI_LINE_32_63(EXTI_InitStruct->Line_32_63));
assert_param(IS_FUNCTIONAL_STATE(EXTI_InitStruct->LineCommand));
assert_param(IS_LL_EXTI_MODE(EXTI_InitStruct->Mode));
/* ENABLE LineCommand */
if (EXTI_InitStruct->LineCommand != DISABLE)
{
assert_param(IS_LL_EXTI_TRIGGER(EXTI_InitStruct->Trigger));
/* Configure EXTI Lines in range from 0 to 31 */
if (EXTI_InitStruct->Line_0_31 != LL_EXTI_LINE_NONE)
{
switch (EXTI_InitStruct->Mode)
{
case LL_EXTI_MODE_IT:
/* First Disable Event on provided Lines */
LL_EXTI_DisableEvent_0_31(EXTI_InitStruct->Line_0_31);
/* Then Enable IT on provided Lines */
LL_EXTI_EnableIT_0_31(EXTI_InitStruct->Line_0_31);
break;
case LL_EXTI_MODE_EVENT:
/* First Disable IT on provided Lines */
LL_EXTI_DisableIT_0_31(EXTI_InitStruct->Line_0_31);
/* Then Enable Event on provided Lines */
LL_EXTI_EnableEvent_0_31(EXTI_InitStruct->Line_0_31);
break;
case LL_EXTI_MODE_IT_EVENT:
/* Directly Enable IT & Event on provided Lines */
LL_EXTI_EnableIT_0_31(EXTI_InitStruct->Line_0_31);
LL_EXTI_EnableEvent_0_31(EXTI_InitStruct->Line_0_31);
break;
default:
status = 0x01u;
break;
}
if (EXTI_InitStruct->Trigger != LL_EXTI_TRIGGER_NONE)
{
switch (EXTI_InitStruct->Trigger)
{
case LL_EXTI_TRIGGER_RISING:
/* First Disable Falling Trigger on provided Lines */
LL_EXTI_DisableFallingTrig_0_31(EXTI_InitStruct->Line_0_31);
/* Then Enable Rising Trigger on provided Lines */
LL_EXTI_EnableRisingTrig_0_31(EXTI_InitStruct->Line_0_31);
break;
case LL_EXTI_TRIGGER_FALLING:
/* First Disable Rising Trigger on provided Lines */
LL_EXTI_DisableRisingTrig_0_31(EXTI_InitStruct->Line_0_31);
/* Then Enable Falling Trigger on provided Lines */
LL_EXTI_EnableFallingTrig_0_31(EXTI_InitStruct->Line_0_31);
break;
case LL_EXTI_TRIGGER_RISING_FALLING:
LL_EXTI_EnableRisingTrig_0_31(EXTI_InitStruct->Line_0_31);
LL_EXTI_EnableFallingTrig_0_31(EXTI_InitStruct->Line_0_31);
break;
default:
status |= 0x02u;
break;
}
}
}
/* Configure EXTI Lines in range from 32 to 63 */
if (EXTI_InitStruct->Line_32_63 != LL_EXTI_LINE_NONE)
{
switch (EXTI_InitStruct->Mode)
{
case LL_EXTI_MODE_IT:
/* First Disable Event on provided Lines */
LL_EXTI_DisableEvent_32_63(EXTI_InitStruct->Line_32_63);
/* Then Enable IT on provided Lines */
LL_EXTI_EnableIT_32_63(EXTI_InitStruct->Line_32_63);
break;
case LL_EXTI_MODE_EVENT:
/* First Disable IT on provided Lines */
LL_EXTI_DisableIT_32_63(EXTI_InitStruct->Line_32_63);
/* Then Enable Event on provided Lines */
LL_EXTI_EnableEvent_32_63(EXTI_InitStruct->Line_32_63);
break;
case LL_EXTI_MODE_IT_EVENT:
/* Directly Enable IT & Event on provided Lines */
LL_EXTI_EnableIT_32_63(EXTI_InitStruct->Line_32_63);
LL_EXTI_EnableEvent_32_63(EXTI_InitStruct->Line_32_63);
break;
default:
status |= 0x04u;
break;
}
if (EXTI_InitStruct->Trigger != LL_EXTI_TRIGGER_NONE)
{
switch (EXTI_InitStruct->Trigger)
{
case LL_EXTI_TRIGGER_RISING:
/* First Disable Falling Trigger on provided Lines */
LL_EXTI_DisableFallingTrig_32_63(EXTI_InitStruct->Line_32_63);
/* Then Enable IT on provided Lines */
LL_EXTI_EnableRisingTrig_32_63(EXTI_InitStruct->Line_32_63);
break;
case LL_EXTI_TRIGGER_FALLING:
/* First Disable Rising Trigger on provided Lines */
LL_EXTI_DisableRisingTrig_32_63(EXTI_InitStruct->Line_32_63);
/* Then Enable Falling Trigger on provided Lines */
LL_EXTI_EnableFallingTrig_32_63(EXTI_InitStruct->Line_32_63);
break;
case LL_EXTI_TRIGGER_RISING_FALLING:
LL_EXTI_EnableRisingTrig_32_63(EXTI_InitStruct->Line_32_63);
LL_EXTI_EnableFallingTrig_32_63(EXTI_InitStruct->Line_32_63);
break;
default:
status = ERROR;
break;
}
}
}
}
/* DISABLE LineCommand */
else
{
/* De-configure EXTI Lines in range from 0 to 31 */
LL_EXTI_DisableIT_0_31(EXTI_InitStruct->Line_0_31);
LL_EXTI_DisableEvent_0_31(EXTI_InitStruct->Line_0_31);
/* De-configure EXTI Lines in range from 32 to 63 */
LL_EXTI_DisableIT_32_63(EXTI_InitStruct->Line_32_63);
LL_EXTI_DisableEvent_32_63(EXTI_InitStruct->Line_32_63);
}
return status;
}
/**
* @brief Set each @ref LL_EXTI_InitTypeDef field to default value.
* @param EXTI_InitStruct Pointer to a @ref LL_EXTI_InitTypeDef structure.
* @retval None
*/
void LL_EXTI_StructInit(LL_EXTI_InitTypeDef *EXTI_InitStruct)
{
EXTI_InitStruct->Line_0_31 = LL_EXTI_LINE_NONE;
EXTI_InitStruct->Line_32_63 = LL_EXTI_LINE_NONE;
EXTI_InitStruct->LineCommand = DISABLE;
EXTI_InitStruct->Mode = LL_EXTI_MODE_IT;
EXTI_InitStruct->Trigger = LL_EXTI_TRIGGER_FALLING;
}
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
#endif /* defined (EXTI) */
/**
* @}
*/
#endif /* USE_FULL_LL_DRIVER */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

File diff suppressed because it is too large Load Diff

View File

@ -26,16 +26,18 @@ static uint8_t *USBD_Composite_GetOtherSpeedCfgDesc (uint16_t *length);
static uint8_t *USBD_Composite_GetDeviceQualifierDescriptor (uint16_t *length); static uint8_t *USBD_Composite_GetDeviceQualifierDescriptor (uint16_t *length);
#define NUM_INTERFACES 2 #define NUM_CLASSES 2
#define NUM_INTERFACES 3
#if NUM_INTERFACES>1 #if NUM_INTERFACES>1
#define COMPOSITE_CDC_HID_DESCRIPTOR_SIZE (90) #define COMPOSITE_CDC_HID_DESCRIPTOR_SIZE (90 + 8+9 + 4)
#else #else
#define COMPOSITE_CDC_HID_DESCRIPTOR_SIZE (41) #define COMPOSITE_CDC_HID_DESCRIPTOR_SIZE (41)
#endif #endif
#define HID_INTF_NUM 0 #define HID_INTF_NUM 0
#define CDC_INTF_NUM 1 #define CDC_MASTER_INTF_NUM 1
#define CDC_SLAVE_INTF_NUM 2
__ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_SIZE] __ALIGN_END = __ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_SIZE] __ALIGN_END =
{ {
/*Configuration Descriptor*/ /*Configuration Descriptor*/
@ -43,7 +45,7 @@ __ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */ USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
COMPOSITE_CDC_HID_DESCRIPTOR_SIZE, /* wTotalLength:no of returned bytes */ COMPOSITE_CDC_HID_DESCRIPTOR_SIZE, /* wTotalLength:no of returned bytes */
0x00, 0x00,
NUM_INTERFACES, /* bNumInterfaces: 1 interface */ NUM_INTERFACES, /* bNumInterfaces */
0x01, /* bConfigurationValue: Configuration value */ 0x01, /* bConfigurationValue: Configuration value */
0x00, /* iConfiguration: Index of string descriptor describing the configuration */ 0x00, /* iConfiguration: Index of string descriptor describing the configuration */
0x80, /* bmAttributes: self powered */ 0x80, /* bmAttributes: self powered */
@ -92,22 +94,28 @@ __ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_
0x00, 0x00,
HID_BINTERVAL, /*bInterval: Polling Interval */ HID_BINTERVAL, /*bInterval: Polling Interval */
#if NUM_INTERFACES > 1 #if NUM_INTERFACES > 1
/* */ /* */
/* CDC */ /* CDC */
/* */ /* */
// This "IAD" is needed for Windows since it ignores the standard Union Functional Descriptor
0x08, // bLength
0x0B, // IAD type
CDC_MASTER_INTF_NUM, // First interface
CDC_SLAVE_INTF_NUM, // Next interface
0x02, // bInterfaceClass of the first interface
0x02, // bInterfaceSubClass of the first interface
0x00, // bInterfaceProtocol of the first interface
0x00, // Interface string index
/*Interface Descriptor */ /*Interface Descriptor */
0x09, /* bLength: Interface Descriptor size */ 0x09, /* bLength: Interface Descriptor size */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */ USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
/* Interface descriptor type */ /* Interface descriptor type */
/*!*/ CDC_INTF_NUM, /* bInterfaceNumber: Number of Interface */ /*!*/ CDC_MASTER_INTF_NUM, /* bInterfaceNumber: Number of Interface */
0x00, /* bAlternateSetting: Alternate setting */ 0x00, /* bAlternateSetting: Alternate setting */
0x03, /* bNumEndpoints: 3 endpoints used */ 0x01, /* bNumEndpoints: 1 endpoint used */
0x02, /* bInterfaceClass: Communication Interface Class */ 0x02, /* bInterfaceClass: Communication Interface Class */
0x02, /* bInterfaceSubClass: Abstract Control Model */ 0x02, /* bInterfaceSubClass: Abstract Control Model */
0x00, /* bInterfaceProtocol: Common AT commands */ 0x00, /* bInterfaceProtocol: Common AT commands */
@ -125,7 +133,7 @@ __ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_
0x24, /* bDescriptorType: CS_INTERFACE */ 0x24, /* bDescriptorType: CS_INTERFACE */
0x01, /* bDescriptorSubtype: Call Management Func Desc */ 0x01, /* bDescriptorSubtype: Call Management Func Desc */
0x00, /* bmCapabilities: D0+D1 */ 0x00, /* bmCapabilities: D0+D1 */
/*!*/ CDC_INTF_NUM, /* bDataInterface: 0 */ /*!*/ CDC_SLAVE_INTF_NUM, /* bDataInterface: 0 */
/*ACM Functional Descriptor*/ /*ACM Functional Descriptor*/
0x04, /* bFunctionLength */ 0x04, /* bFunctionLength */
@ -137,10 +145,10 @@ __ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_
0x05, /* bFunctionLength */ 0x05, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE */ 0x24, /* bDescriptorType: CS_INTERFACE */
0x06, /* bDescriptorSubtype: Union func desc */ 0x06, /* bDescriptorSubtype: Union func desc */
/*!*/ CDC_INTF_NUM, /* bMasterInterface: Communication class interface */ /*!*/ CDC_MASTER_INTF_NUM, /* bMasterInterface: Communication class interface */
/*!*/ CDC_INTF_NUM, /* bSlaveInterface0: Data Class Interface */ /*!*/ CDC_SLAVE_INTF_NUM, /* bSlaveInterface0: Data Class Interface */
/*Endpoint 2 Descriptor*/ /* Control Endpoint Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */ 0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */ USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDC_CMD_EP, /* bEndpointAddress */ CDC_CMD_EP, /* bEndpointAddress */
@ -149,6 +157,17 @@ __ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_
HIBYTE(CDC_CMD_PACKET_SIZE), HIBYTE(CDC_CMD_PACKET_SIZE),
0x10, /* bInterval: */ 0x10, /* bInterval: */
/* Interface descriptor */
0x09, /* bLength */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType */
CDC_SLAVE_INTF_NUM, /* bInterfaceNumber */
0x00, /* bAlternateSetting */
0x02, /* bNumEndpoints */
0x0A, /* bInterfaceClass: Communication class data */
0x00, /* bInterfaceSubClass */
0x00, /* bInterfaceProtocol */
0x00,
/*Endpoint OUT Descriptor*/ /*Endpoint OUT Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */ 0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */ USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
@ -167,10 +186,13 @@ __ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
0x00, /* bInterval: ignore for Bulk transfer */ 0x00, /* bInterval: ignore for Bulk transfer */
4, /* Descriptor size */
3, /* Descriptor type */
0x09,
0x04,
#endif #endif
}; };
USBD_ClassTypeDef USBD_Composite = USBD_ClassTypeDef USBD_Composite =
{ {
USBD_Composite_Init, USBD_Composite_Init,
@ -195,14 +217,27 @@ int in_endpoint_to_class[MAX_ENDPOINTS];
int out_endpoint_to_class[MAX_ENDPOINTS]; int out_endpoint_to_class[MAX_ENDPOINTS];
void USBD_Composite_Set_Classes(USBD_ClassTypeDef *class0, USBD_ClassTypeDef *class1) { void USBD_Composite_Set_Classes(USBD_ClassTypeDef *hid_class, USBD_ClassTypeDef *cdc_class) {
USBD_Classes[0] = class0; USBD_Classes[0] = hid_class;
USBD_Classes[1] = class1; USBD_Classes[1] = cdc_class;
}
static USBD_ClassTypeDef * getClass(uint8_t index)
{
switch(index)
{
case HID_INTF_NUM:
return USBD_Classes[0];
case CDC_MASTER_INTF_NUM:
case CDC_SLAVE_INTF_NUM:
return USBD_Classes[1];
}
return NULL;
} }
static uint8_t USBD_Composite_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx) { static uint8_t USBD_Composite_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx) {
int i; int i;
for(i = 0; i < NUM_INTERFACES; i++) { for(i = 0; i < NUM_CLASSES; i++) {
if (USBD_Classes[i]->Init(pdev, cfgidx) != USBD_OK) { if (USBD_Classes[i]->Init(pdev, cfgidx) != USBD_OK) {
return USBD_FAIL; return USBD_FAIL;
} }
@ -213,7 +248,7 @@ static uint8_t USBD_Composite_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx) {
static uint8_t USBD_Composite_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx) { static uint8_t USBD_Composite_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx) {
int i; int i;
for(i = 0; i < NUM_INTERFACES; i++) { for(i = 0; i < NUM_CLASSES; i++) {
if (USBD_Classes[i]->DeInit(pdev, cfgidx) != USBD_OK) { if (USBD_Classes[i]->DeInit(pdev, cfgidx) != USBD_OK) {
return USBD_FAIL; return USBD_FAIL;
} }
@ -224,10 +259,13 @@ static uint8_t USBD_Composite_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx)
static uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { static uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) {
int i; int i;
USBD_ClassTypeDef * device_class;
device_class = getClass(req->wIndex);
switch (req->bmRequest & USB_REQ_TYPE_MASK) { switch (req->bmRequest & USB_REQ_TYPE_MASK) {
case USB_REQ_TYPE_CLASS : case USB_REQ_TYPE_CLASS :
if (req->wIndex < NUM_INTERFACES) if (device_class != NULL)
return USBD_Classes[req->wIndex]->Setup(pdev, req); return device_class->Setup(pdev, req);
else else
return USBD_FAIL; return USBD_FAIL;
@ -236,7 +274,7 @@ static uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqType
switch (req->bRequest) { switch (req->bRequest) {
case USB_REQ_GET_DESCRIPTOR : case USB_REQ_GET_DESCRIPTOR :
for(i = 0; i < NUM_INTERFACES; i++) { for(i = 0; i < NUM_CLASSES; i++) {
if (USBD_Classes[i]->Setup(pdev, req) != USBD_OK) { if (USBD_Classes[i]->Setup(pdev, req) != USBD_OK) {
return USBD_FAIL; return USBD_FAIL;
} }
@ -246,8 +284,8 @@ static uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqType
case USB_REQ_GET_INTERFACE : case USB_REQ_GET_INTERFACE :
case USB_REQ_SET_INTERFACE : case USB_REQ_SET_INTERFACE :
if (req->wIndex < NUM_INTERFACES) if (device_class != NULL)
return USBD_Classes[req->wIndex]->Setup(pdev, req); return device_class->Setup(pdev, req);
else else
return USBD_FAIL; return USBD_FAIL;
} }
@ -274,7 +312,7 @@ static uint8_t USBD_Composite_DataOut (USBD_HandleTypeDef *pdev, uint8_t epnum)
static uint8_t USBD_Composite_EP0_RxReady (USBD_HandleTypeDef *pdev) { static uint8_t USBD_Composite_EP0_RxReady (USBD_HandleTypeDef *pdev) {
int i; int i;
for(i = 0; i < NUM_INTERFACES; i++) { for(i = 0; i < NUM_CLASSES; i++) {
if (USBD_Classes[i]->EP0_RxReady != NULL) { if (USBD_Classes[i]->EP0_RxReady != NULL) {
if (USBD_Classes[i]->EP0_RxReady(pdev) != USBD_OK) { if (USBD_Classes[i]->EP0_RxReady(pdev) != USBD_OK) {
return USBD_FAIL; return USBD_FAIL;

View File

@ -38,6 +38,8 @@ void wait_for_usb_tether();
uint32_t __90_ms = 0; uint32_t __90_ms = 0;
uint32_t __last_button_press_time = 0;
uint32_t __last_button_bounce_time = 0;
uint32_t __device_status = 0; uint32_t __device_status = 0;
uint32_t __last_update = 0; uint32_t __last_update = 0;
extern PCD_HandleTypeDef hpcd; extern PCD_HandleTypeDef hpcd;
@ -75,6 +77,21 @@ void TIM6_DAC_IRQHandler()
ctaphid_update_status(__device_status); ctaphid_update_status(__device_status);
} }
} }
if (is_touch_button_pressed == IS_BUTTON_PRESSED)
{
if (IS_BUTTON_PRESSED())
{
// Only allow 1 press per 25 ms.
if ((millis() - __last_button_bounce_time) > 25)
{
__last_button_press_time = millis();
}
__last_button_bounce_time = millis();
}
}
#ifndef IS_BOOTLOADER #ifndef IS_BOOTLOADER
// NFC sending WTX if needs // NFC sending WTX if needs
if (device_is_nfc() == NFC_IS_ACTIVE) if (device_is_nfc() == NFC_IS_ACTIVE)
@ -84,6 +101,21 @@ void TIM6_DAC_IRQHandler()
#endif #endif
} }
// Interrupt on rising edge of button (button released)
void EXTI0_IRQHandler(void)
{
EXTI->PR1 = EXTI->PR1;
if (is_physical_button_pressed == IS_BUTTON_PRESSED)
{
// Only allow 1 press per 25 ms.
if ((millis() - __last_button_bounce_time) > 25)
{
__last_button_press_time = millis();
}
__last_button_bounce_time = millis();
}
}
// Global USB interrupt handler // Global USB interrupt handler
void USB_IRQHandler(void) void USB_IRQHandler(void)
{ {
@ -493,6 +525,41 @@ static int handle_packets()
return 0; return 0;
} }
static int wait_for_button_activate(uint32_t wait)
{
int ret;
uint32_t start = millis();
do
{
if ((start + wait) < millis())
{
return 0;
}
delay(1);
ret = handle_packets();
if (ret)
return ret;
} while (!IS_BUTTON_PRESSED());
return 0;
}
static int wait_for_button_release(uint32_t wait)
{
int ret;
uint32_t start = millis();
do
{
if ((start + wait) < millis())
{
return 0;
}
delay(1);
ret = handle_packets();
if (ret)
return ret;
} while (IS_BUTTON_PRESSED());
return 0;
}
int ctap_user_presence_test(uint32_t up_delay) int ctap_user_presence_test(uint32_t up_delay)
{ {
int ret; int ret;
@ -500,6 +567,7 @@ int ctap_user_presence_test(uint32_t up_delay)
{ {
return 1; return 1;
} }
#if SKIP_BUTTON_CHECK_WITH_DELAY #if SKIP_BUTTON_CHECK_WITH_DELAY
int i=500; int i=500;
while(i--) while(i--)
@ -512,53 +580,41 @@ int ctap_user_presence_test(uint32_t up_delay)
#elif SKIP_BUTTON_CHECK_FAST #elif SKIP_BUTTON_CHECK_FAST
delay(2); delay(2);
ret = handle_packets(); ret = handle_packets();
if (ret) return ret; if (ret)
return ret;
goto done; goto done;
#endif #endif
uint32_t t1 = millis();
// If button was pressed within last [2] seconds, succeed.
if (__last_button_press_time && (millis() - __last_button_press_time < 2000))
{
goto done;
}
// Set LED status and wait.
led_rgb(0xff3520); led_rgb(0xff3520);
if (IS_BUTTON_PRESSED == is_touch_button_pressed) // Block and wait for some time.
{ ret = wait_for_button_activate(up_delay);
// Wait for user to release touch button if it's already pressed
while (IS_BUTTON_PRESSED())
{
if (t1 + up_delay < millis())
{
printf1(TAG_GEN,"Button not pressed\n");
goto fail;
}
ret = handle_packets();
if (ret) return ret; if (ret) return ret;
} ret = wait_for_button_release(up_delay);
}
t1 = millis();
do
{
if (t1 + up_delay < millis())
{
goto fail;
}
delay(1);
ret = handle_packets();
if (ret) return ret; if (ret) return ret;
// If button was pressed within last [2] seconds, succeed.
if (__last_button_press_time && (millis() - __last_button_press_time < 2000))
{
goto done;
} }
while (! IS_BUTTON_PRESSED());
led_rgb(0x001040);
delay(50);
#if SKIP_BUTTON_CHECK_WITH_DELAY || SKIP_BUTTON_CHECK_FAST return 0;
done: done:
#endif ret = wait_for_button_release(up_delay);
__last_button_press_time = 0;
return 1; return 1;
fail:
return 0;
} }
int ctap_generate_rng(uint8_t * dst, size_t num) int ctap_generate_rng(uint8_t * dst, size_t num)

View File

@ -20,6 +20,7 @@
#include "stm32l4xx_ll_rng.h" #include "stm32l4xx_ll_rng.h"
#include "stm32l4xx_ll_spi.h" #include "stm32l4xx_ll_spi.h"
#include "stm32l4xx_ll_usb.h" #include "stm32l4xx_ll_usb.h"
#include "stm32l4xx_ll_exti.h"
#include "stm32l4xx_hal_pcd.h" #include "stm32l4xx_hal_pcd.h"
#include "stm32l4xx_hal.h" #include "stm32l4xx_hal.h"
@ -849,19 +850,17 @@ void init_gpio(void)
LL_GPIO_SetPinMode(SOLO_BUTTON_PORT,SOLO_BUTTON_PIN,LL_GPIO_MODE_INPUT); LL_GPIO_SetPinMode(SOLO_BUTTON_PORT,SOLO_BUTTON_PIN,LL_GPIO_MODE_INPUT);
LL_GPIO_SetPinPull(SOLO_BUTTON_PORT,SOLO_BUTTON_PIN,LL_GPIO_PULL_UP); LL_GPIO_SetPinPull(SOLO_BUTTON_PORT,SOLO_BUTTON_PIN,LL_GPIO_PULL_UP);
#ifdef SOLO_AMS_IRQ_PORT #ifndef IS_BOOTLOADER
// SAVE POWER LL_SYSCFG_SetEXTISource(LL_SYSCFG_EXTI_PORTA, LL_SYSCFG_EXTI_LINE0);
// LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC); LL_EXTI_InitTypeDef EXTI_InitStruct;
// /**/ EXTI_InitStruct.Line_0_31 = LL_EXTI_LINE_0; // GPIOA_0
// LL_GPIO_InitTypeDef GPIO_InitStruct; EXTI_InitStruct.Line_32_63 = LL_EXTI_LINE_NONE;
// GPIO_InitStruct.Pin = SOLO_AMS_IRQ_PIN; EXTI_InitStruct.LineCommand = ENABLE;
// GPIO_InitStruct.Mode = LL_GPIO_MODE_INPUT; EXTI_InitStruct.Mode = LL_EXTI_MODE_IT;
// GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; EXTI_InitStruct.Trigger = LL_EXTI_TRIGGER_RISING;
// LL_GPIO_Init(SOLO_AMS_IRQ_PORT, &GPIO_InitStruct); LL_EXTI_Init(&EXTI_InitStruct);
//
// NVIC_EnableIRQ(EXTI0_IRQn);
// LL_GPIO_SetPinMode(SOLO_AMS_IRQ_PORT,SOLO_AMS_IRQ_PIN,LL_GPIO_MODE_INPUT);
// LL_GPIO_SetPinPull(SOLO_AMS_IRQ_PORT,SOLO_AMS_IRQ_PIN,LL_GPIO_PULL_UP);
#endif #endif
} }

View File

@ -92,19 +92,27 @@ int nfc_init()
return NFC_IS_NA; return NFC_IS_NA;
} }
static uint8_t gl_int0 = 0;
void process_int0(uint8_t int0) void process_int0(uint8_t int0)
{ {
gl_int0 = int0;
} }
bool ams_wait_for_tx(uint32_t timeout_ms) bool ams_wait_for_tx(uint32_t timeout_ms)
{ {
if (gl_int0 & AMS_INT_TXE) {
uint8_t int0 = ams_read_reg(AMS_REG_INT0);
process_int0(int0);
return true;
}
uint32_t tstart = millis(); uint32_t tstart = millis();
while (tstart + timeout_ms > millis()) while (tstart + timeout_ms > millis())
{ {
uint8_t int0 = ams_read_reg(AMS_REG_INT0); uint8_t int0 = ams_read_reg(AMS_REG_INT0);
if (int0) process_int0(int0); process_int0(int0);
if (int0 & AMS_INT_TXE) if (int0 & AMS_INT_TXE || int0 & AMS_INT_RXE)
return true; return true;
delay(1); delay(1);
@ -121,8 +129,13 @@ bool ams_receive_with_timeout(uint32_t timeout_ms, uint8_t * data, int maxlen, i
uint32_t tstart = millis(); uint32_t tstart = millis();
while (tstart + timeout_ms > millis()) while (tstart + timeout_ms > millis())
{ {
uint8_t int0 = ams_read_reg(AMS_REG_INT0); uint8_t int0 = 0;
if (int0) process_int0(int0); if (gl_int0 & AMS_INT_RXE) {
int0 = gl_int0;
} else {
int0 = ams_read_reg(AMS_REG_INT0);
process_int0(int0);
}
uint8_t buffer_status2 = ams_read_reg(AMS_REG_BUF2); uint8_t buffer_status2 = ams_read_reg(AMS_REG_BUF2);
if (buffer_status2 && (int0 & AMS_INT_RXE)) if (buffer_status2 && (int0 & AMS_INT_RXE))
@ -196,7 +209,6 @@ bool nfc_write_response(uint8_t req0, uint16_t resp)
void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len) void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
{ {
uint8_t res[32 + 2]; uint8_t res[32 + 2];
int sendlen = 0;
uint8_t iBlock = NFC_CMD_IBLOCK | (req0 & 0x0f); uint8_t iBlock = NFC_CMD_IBLOCK | (req0 & 0x0f);
uint8_t block_offset = p14443_block_offset(req0); uint8_t block_offset = p14443_block_offset(req0);
@ -208,6 +220,7 @@ void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
memcpy(&res[block_offset], data, len); memcpy(&res[block_offset], data, len);
nfc_write_frame(res, len + block_offset); nfc_write_frame(res, len + block_offset);
} else { } else {
int sendlen = 0;
do { do {
// transmit I block // transmit I block
int vlen = MIN(32 - block_offset, len - sendlen); int vlen = MIN(32 - block_offset, len - sendlen);
@ -227,11 +240,11 @@ void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
sendlen += vlen; sendlen += vlen;
// wait for transmit (32 bytes aprox 2,5ms) // wait for transmit (32 bytes aprox 2,5ms)
// if (!ams_wait_for_tx(10)) if (!ams_wait_for_tx(5))
// { {
// printf1(TAG_NFC, "TX timeout. slen: %d \r\n", sendlen); printf1(TAG_NFC, "TX timeout. slen: %d \r\n", sendlen);
// break; break;
// } }
// if needs to receive R block (not a last block) // if needs to receive R block (not a last block)
if (res[0] & 0x10) if (res[0] & 0x10)
@ -316,7 +329,7 @@ bool WTX_off()
void WTX_timer_exec() void WTX_timer_exec()
{ {
// condition: (timer on) or (not expired[300ms]) // condition: (timer on) or (not expired[300ms])
if ((WTX_timer <= 0) || WTX_timer + 300 > millis()) if ((WTX_timer == 0) || WTX_timer + 300 > millis())
return; return;
WTX_process(10); WTX_process(10);
@ -327,12 +340,12 @@ void WTX_timer_exec()
// read timeout must be 10 ms to call from interrupt // read timeout must be 10 ms to call from interrupt
bool WTX_process(int read_timeout) bool WTX_process(int read_timeout)
{ {
uint8_t wtx[] = {0xf2, 0x01};
if (WTX_fail) if (WTX_fail)
return false; return false;
if (!WTX_sent) if (!WTX_sent)
{ {
uint8_t wtx[] = {0xf2, 0x01};
nfc_write_frame(wtx, sizeof(wtx)); nfc_write_frame(wtx, sizeof(wtx));
WTX_sent = true; WTX_sent = true;
return true; return true;
@ -618,7 +631,7 @@ void nfc_process_iblock(uint8_t * buf, int len)
if (!WTX_off()) if (!WTX_off())
return; return;
printf1(TAG_NFC, "CTAP resp: 0x%02<EFBFBD> len: %d\r\n", status, ctap_resp.length); printf1(TAG_NFC, "CTAP resp: 0x%02x len: %d\r\n", status, ctap_resp.length);
if (status == CTAP1_ERR_SUCCESS) if (status == CTAP1_ERR_SUCCESS)
{ {
@ -687,7 +700,14 @@ void nfc_process_block(uint8_t * buf, unsigned int len)
if (IS_PPSS_CMD(buf[0])) if (IS_PPSS_CMD(buf[0]))
{ {
printf1(TAG_NFC, "NFC_CMD_PPSS\r\n"); printf1(TAG_NFC, "NFC_CMD_PPSS [%d] 0x%02x\r\n", len, (len > 2) ? buf[2] : 0);
if (buf[1] == 0x11 && (buf[2] & 0x0f) == 0x00) {
nfc_write_frame(buf, 1); // ack with correct start byte
} else {
printf1(TAG_NFC, "NFC_CMD_PPSS ERROR!!!\r\n");
nfc_write_frame((uint8_t*)"\x00", 1); // this should not happend. but iso14443-4 dont have NACK here, so just 0x00
}
} }
else if (IS_IBLOCK(buf[0])) else if (IS_IBLOCK(buf[0]))
{ {
@ -779,6 +799,8 @@ int nfc_loop()
read_reg_block(&ams); read_reg_block(&ams);
uint8_t old_int0 = gl_int0;
process_int0(ams.regs.int0);
uint8_t state = AMS_STATE_MASK & ams.regs.rfid_status; uint8_t state = AMS_STATE_MASK & ams.regs.rfid_status;
if (state != AMS_STATE_SELECTED && state != AMS_STATE_SELECTEDX) if (state != AMS_STATE_SELECTED && state != AMS_STATE_SELECTEDX)
@ -792,7 +814,7 @@ int nfc_loop()
// if (state != AMS_STATE_SENSE) // if (state != AMS_STATE_SENSE)
// printf1(TAG_NFC," %s x%02x\r\n", ams_get_state_string(ams.regs.rfid_status), state); // printf1(TAG_NFC," %s x%02x\r\n", ams_get_state_string(ams.regs.rfid_status), state);
} }
if (ams.regs.int0 & AMS_INT_INIT) if (ams.regs.int0 & AMS_INT_INIT || old_int0 & AMS_INT_INIT)
{ {
nfc_state_init(); nfc_state_init();
} }
@ -801,7 +823,7 @@ int nfc_loop()
// ams_print_int1(ams.regs.int1); // ams_print_int1(ams.regs.int1);
} }
if ((ams.regs.int0 & AMS_INT_RXE)) if (ams.regs.int0 & AMS_INT_RXE || old_int0 & AMS_INT_RXE)
{ {
if (ams.regs.buffer_status2) if (ams.regs.buffer_status2)
{ {