Merge pull request #10 from derskythe/dev-master

Sync
This commit is contained in:
DerSkythe 2023-08-19 08:30:37 +04:00 committed by GitHub
commit e7c52828ea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
1597 changed files with 2746 additions and 145613 deletions

View file

@ -161,3 +161,10 @@ Max butthurt: 5
Min level: 1
Max level: 3
Weight: 4
Name: L1_Sad_song_128x64
Min butthurt: 8
Max butthurt: 13
Min level: 1
Max level: 3
Weight: 4

View file

@ -102,8 +102,8 @@ index 0000000..269b544
+ .settings_is_loaded = false};
+
+static const RGBBacklightColor colors[] = {
+ {"Orange", 255, 79, 0},
+ {"Yellow", 255, 170, 0},
+ {"Orange", 255, 60, 0},
+ {"Yellow", 255, 150, 0},
+ {"Spring", 167, 255, 0},
+ {"Lime", 0, 255, 0},
+ {"Aqua", 0, 255, 127},
@ -114,7 +114,7 @@ index 0000000..269b544
+ {"Magenta", 210, 0, 210},
+ {"Pink", 255, 0, 127},
+ {"Red", 255, 0, 0},
+ {"White", 140, 140, 140},
+ {"White", 150, 150, 110},
+};
+
+uint8_t rgb_backlight_get_color_count(void) {

View file

@ -15,16 +15,38 @@ steps:
- git submodule foreach git config --local gc.auto 0
- git log -1 --format='%H'
- name: "Build firmware"
- name: "Build clean"
image: hfdj/fztools
pull: never
commands:
- export DIST_SUFFIX=${DRONE_TAG}c
- export WORKFLOW_BRANCH_OR_TAG=release-cfw
- export FORCE_NO_DIRTY=yes
- rm -rf assets/resources/apps/
- rm -rf build/
- rm -rf dist/
- rm -rf .sconsign.dblite
- ./fbt COMPACT=1 DEBUG=0 updater_package
- mkdir artifacts-clean
- mv dist/f7-C/* artifacts-clean/
- ls -laS artifacts-clean
- ls -laS artifacts-clean/f7-update-${DRONE_TAG}c
environment:
FBT_TOOLS_CUSTOM_LINK:
from_secret: fbt_link
- name: "Build default"
image: hfdj/fztools
pull: never
commands:
- export DIST_SUFFIX=${DRONE_TAG}
- export WORKFLOW_BRANCH_OR_TAG=release-cfw
- rm -rf assets/resources/apps/
- rm -rf build/
- rm -rf dist/
- rm -rf .sconsign.dblite
- export FORCE_NO_DIRTY=yes
- wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-base.tgz
- tar zxvf all-the-apps-base.tgz
- cp -R base_pack_build/artifacts-base/* assets/resources/apps/
- rm -rf base_pack_build
- rm -rf all-the-apps-base.tgz
- ./fbt COMPACT=1 DEBUG=0 updater_package
- mkdir artifacts-default
- mv dist/f7-C/* artifacts-default/
@ -38,9 +60,10 @@ steps:
image: hfdj/fztools
pull: never
commands:
- git clone https://github.com/xMasterX/all-the-plugins.git
- cp -R all-the-plugins/apps/* assets/resources/apps/
- rm -rf all-the-plugins
- wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-extra.tgz
- tar zxvf all-the-apps-extra.tgz
- cp -R extra_pack_build/artifacts-extra/* assets/resources/apps/
- rm -rf extra_pack_build
- export DIST_SUFFIX=${DRONE_TAG}e
- export WORKFLOW_BRANCH_OR_TAG=release-cfw
- export FORCE_NO_DIRTY=yes
@ -86,6 +109,13 @@ steps:
- export FORCE_NO_DIRTY=yes
- rm -f build/f7-firmware-C/toolbox/version.*
- ./fbt COMPACT=1 DEBUG=0 updater_package
- wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-base.tgz
- tar zxvf all-the-apps-base.tgz
- cp -R base_pack_build/artifacts-base/* assets/resources/apps/
- rm -rf base_pack_build
- rm -rf all-the-apps-base.tgz
- rm -f build/f7-firmware-C/toolbox/version.*
- ./fbt COMPACT=1 DEBUG=0 updater_package
- mkdir artifacts-ofw-anims
- mv dist/f7-C/* artifacts-ofw-anims/
- ls -laS artifacts-ofw-anims
@ -93,6 +123,8 @@ steps:
- sed -i 's/(version)/'${DRONE_TAG}'/g' CHANGELOG.md
- echo '# [Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw/${DRONE_TAG}/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=release-cfw&version='${DRONE_TAG}')' >> CHANGELOG.md
- echo '' >> CHANGELOG.md
- echo '### [Version with only main apps - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'c.tgz&channel=release-cfw&version='${DRONE_TAG}'c)' >> CHANGELOG.md
- echo '' >> CHANGELOG.md
- echo '### [Version without custom animations - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)' >> CHANGELOG.md
- echo '' >> CHANGELOG.md
- echo '### [Version with RGB patch - only for hardware mod! - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz&channel=release-cfw&version='${DRONE_TAG}'r)' >> CHANGELOG.md
@ -107,19 +139,23 @@ steps:
commands:
- cp artifacts-extra-apps/flipper-z-f7-update-${DRONE_TAG}e.tgz .
- cp artifacts-rgb-patch/flipper-z-f7-update-${DRONE_TAG}r.tgz .
- cp artifacts-clean/flipper-z-f7-update-${DRONE_TAG}c.tgz .
- cp artifacts-ofw-anims/flipper-z-f7-update-${DRONE_TAG}n.tgz .
- cp artifacts-default/flipper-z-f7-update-${DRONE_TAG}.tgz .
- zip -r artifacts-extra-apps/flipper-z-f7-update-${DRONE_TAG}e.zip artifacts-extra-apps/f7-update-${DRONE_TAG}e
- zip -r artifacts-rgb-patch/flipper-z-f7-update-${DRONE_TAG}r.zip artifacts-rgb-patch/f7-update-${DRONE_TAG}r
- zip -r artifacts-clean/flipper-z-f7-update-${DRONE_TAG}c.zip artifacts-clean/f7-update-${DRONE_TAG}c
- zip -r artifacts-ofw-anims/flipper-z-f7-update-${DRONE_TAG}n.zip artifacts-ofw-anims/f7-update-${DRONE_TAG}n
- zip -r artifacts-default/flipper-z-f7-update-${DRONE_TAG}.zip artifacts-default/f7-update-${DRONE_TAG}
- tar czpf artifacts-default/flipper-z-any-scripts-${DRONE_TAG}.tgz scripts
- rm -rf artifacts-extra-apps/f7-update-${DRONE_TAG}
- rm -rf artifacts-rgb-patch/f7-update-${DRONE_TAG}
- rm -rf artifacts-clean/f7-update-${DRONE_TAG}
- rm -rf artifacts-ofw-anims/f7-update-${DRONE_TAG}
- rm -rf artifacts-default/f7-update-${DRONE_TAG}
- ls -laS artifacts-extra-apps
- ls -laS artifacts-rgb-patch
- ls -laS artifacts-clean
- ls -laS artifacts-ofw-anims
- ls -laS artifacts-default
- mv artifacts-default/ ${DRONE_TAG}
@ -191,6 +227,21 @@ steps:
from_secret: dep_target_extra
source: flipper-z-f7-update-${DRONE_TAG}r.tgz
- name: "Upload clean version to updates srv"
image: appleboy/drone-scp:linux-amd64
settings:
host:
from_secret: dep_host
username:
from_secret: dep_user
password:
from_secret: dep_passwd
port:
from_secret: dep_port
target:
from_secret: dep_target_extra
source: flipper-z-f7-update-${DRONE_TAG}c.tgz
- name: "Do Github release"
image: ddplugins/github-release
pull: never
@ -206,6 +257,7 @@ steps:
- artifacts-ofw-anims/*.tgz
- artifacts-extra-apps/*.tgz
- artifacts-rgb-patch/*.tgz
- artifacts-clean/*.tgz
title: ${DRONE_TAG}
note: CHANGELOG.md
checksum:
@ -247,12 +299,15 @@ steps:
[-How to install firmware-](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)
[-Download latest extra apps pack-](https://github.com/xMasterX/all-the-plugins/archive/refs/heads/main.zip)
[-Download latest extra apps pack-](https://github.com/xMasterX/all-the-plugins/releases/latest)
[-Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/${DRONE_TAG}/flipper-z-f7-update-${DRONE_TAG}.tgz&channel=release-cfw&version=${DRONE_TAG})
[-Version with only main apps - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_TAG}c.tgz&channel=release-cfw&version=${DRONE_TAG}c)
[-Version without custom animations - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-${DRONE_TAG}n.tgz&channel=release-cfw&version=${DRONE_TAG}n)
@ -275,7 +330,19 @@ steps:
commands:
- wget "https://raw.githubusercontent.com/fieu/discord.sh/e1dc1a7595efad2cad8f072f0b3531c470f5b7c8/discord.sh"
- chmod +x ./discord.sh
- ./discord.sh --text 'New Unleashed firmware released!\n\nVersion - '${DRONE_TAG}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[[Github - Changelog]](https://github.com/DarkFlippers/unleashed-firmware/releases/tag/'${DRONE_TAG}')\n\n[-How to install firmware-](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)\n\n[-Download latest extra apps pack-](https://github.com/xMasterX/all-the-plugins/archive/refs/heads/main.zip)\n\n[-Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/'${DRONE_TAG}'/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=release-cfw&version='${DRONE_TAG}')\n\n[-Version without custom animations - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)\n\n[-Version with RGB patch - only for hardware mod! - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz&channel=release-cfw&version='${DRONE_TAG}'r)\n\n[-Version with RGB patch - only for hardware mod! - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz)\n\n[-Version with Extra apps - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&channel=release-cfw&version='${DRONE_TAG}'e)'
- ./discord.sh --text 'New Unleashed firmware released!\n\nVersion - '${DRONE_TAG}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[[Github - Changelog]](https://github.com/DarkFlippers/unleashed-firmware/releases/tag/'${DRONE_TAG}')\n\n[-How to install firmware-](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)\n\n[-Download latest extra apps pack-](https://github.com/xMasterX/all-the-plugins/releases/latest)\n\n[-Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/'${DRONE_TAG}'/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=release-cfw&version='${DRONE_TAG}')\n\n[-Version with only main apps - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'c.tgz&channel=release-cfw&version='${DRONE_TAG}'c)\n\n[-Version without custom animations - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)\n\n[-Version with RGB patch - only for hardware mod! - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz&channel=release-cfw&version='${DRONE_TAG}'r)\n\n[-Version with RGB patch - only for hardware mod! - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz)\n\n[-Version with Extra apps - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&channel=release-cfw&version='${DRONE_TAG}'e)'
- name: "Send clean build to telegram"
image: appleboy/drone-telegram
settings:
token:
from_secret: tgtoken
to:
from_secret: tgid
format: markdown
message: "Build with only main apps:"
document:
- flipper-z-f7-update-${DRONE_TAG}c.tgz
- name: "Send extra pack build to telegram"
image: appleboy/drone-telegram
@ -314,16 +381,39 @@ steps:
- git submodule foreach git config --local gc.auto 0
- git log -1 --format='%H'
- name: "Build dev FW"
- name: "Build dev clean"
image: hfdj/fztools
pull: never
commands:
- export DIST_SUFFIX=${DRONE_BUILD_NUMBER}c
- export WORKFLOW_BRANCH_OR_TAG=dev-cfw
- export FORCE_NO_DIRTY=yes
- rm -rf assets/resources/apps/
- rm -rf build/
- rm -rf dist/
- rm -rf .sconsign.dblite
- ./fbt COMPACT=1 DEBUG=0 updater_package
- mkdir artifacts-clean
- mv dist/f7-C/* artifacts-clean/
- ls -laS artifacts-clean
- ls -laS artifacts-clean/f7-update-${DRONE_BUILD_NUMBER}c
environment:
FBT_TOOLS_CUSTOM_LINK:
from_secret: fbt_link
- name: "Build dev default"
image: hfdj/fztools
pull: never
commands:
- export DIST_SUFFIX=${DRONE_BUILD_NUMBER}
- export WORKFLOW_BRANCH_OR_TAG=dev-cfw
- rm -rf assets/resources/apps/
- rm -rf build/
- rm -rf dist/
- rm -rf .sconsign.dblite
- export FORCE_NO_DIRTY=yes
- wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-base.tgz
- tar zxvf all-the-apps-base.tgz
- cp -R base_pack_build/artifacts-base/* assets/resources/apps/
- rm -rf base_pack_build
- rm -rf all-the-apps-base.tgz
- ./fbt COMPACT=1 DEBUG=0 updater_package
- mkdir artifacts-default
- mv dist/f7-C/* artifacts-default/
@ -337,9 +427,10 @@ steps:
image: hfdj/fztools
pull: never
commands:
- git clone --branch dev https://github.com/xMasterX/all-the-plugins.git
- cp -R all-the-plugins/apps/* assets/resources/apps/
- rm -rf all-the-plugins
- wget https://github.com/xMasterX/all-the-plugins/releases/latest/download/all-the-apps-extra.tgz
- tar zxvf all-the-apps-extra.tgz
- cp -R extra_pack_build/artifacts-extra/* assets/resources/apps/
- rm -rf extra_pack_build
- export DIST_SUFFIX=${DRONE_BUILD_NUMBER}e
- export WORKFLOW_BRANCH_OR_TAG=dev-cfw
- export FORCE_NO_DIRTY=yes
@ -376,6 +467,7 @@ steps:
commands:
- cp artifacts-extra-apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}e.tgz .
- cp artifacts-rgb-patch/flipper-z-f7-update-${DRONE_BUILD_NUMBER}r.tgz .
- cp artifacts-clean/flipper-z-f7-update-${DRONE_BUILD_NUMBER}c.tgz .
- cp artifacts-default/flipper-z-f7-update-${DRONE_BUILD_NUMBER}.tgz .
- rm -rf artifacts-default/f7-update-${DRONE_BUILD_NUMBER}
- ls -laS artifacts-default
@ -448,6 +540,21 @@ steps:
from_secret: dep_target_extra
source: flipper-z-f7-update-${DRONE_BUILD_NUMBER}r.tgz
- name: "Upload clean version to updates srv"
image: appleboy/drone-scp:linux-amd64
settings:
host:
from_secret: dep_host
username:
from_secret: dep_user
password:
from_secret: dep_passwd
port:
from_secret: dep_port
target:
from_secret: dep_target_extra
source: flipper-z-f7-update-${DRONE_BUILD_NUMBER}c.tgz
- name: "Trigger update server reindex"
image: hfdj/fztools
pull: never
@ -482,6 +589,9 @@ steps:
[-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-${DRONE_BUILD_NUMBER}.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER})
[-Version with only main apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}c.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER}c)
[-Version with RGB patch - only for hardware mod! - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}r.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER}r)
@ -502,6 +612,18 @@ steps:
document:
- dev/flipper-z-f7-update-${DRONE_BUILD_NUMBER}.tgz
- name: "Send clean build to telegram"
image: appleboy/drone-telegram
settings:
token:
from_secret: tgtoken
to:
from_secret: tgid_dev
format: markdown
message: "Clean (Main apps only) Build:"
document:
- flipper-z-f7-update-${DRONE_BUILD_NUMBER}c.tgz
- name: "Send extra pack build to telegram"
image: appleboy/drone-telegram
settings:
@ -523,7 +645,7 @@ steps:
commands:
- wget "https://raw.githubusercontent.com/fieu/discord.sh/e1dc1a7595efad2cad8f072f0b3531c470f5b7c8/discord.sh"
- chmod +x ./discord.sh
- ./discord.sh --text 'Unleashed firmware dev build successful!\n\nBuild - '${DRONE_BUILD_NUMBER}'\n\nCommit - https://github.com/DarkFlippers/unleashed-firmware/commit/'${DRONE_COMMIT_SHA}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[-Version with Extra apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'e.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'e)\n\n[-Version with RGB patch - only for hardware mod! - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'r.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'r)\n\n[-Version with RGB patch - only for hardware mod! - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'r.tgz)\n\n[-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}')'
- ./discord.sh --text 'Unleashed firmware dev build successful!\n\nBuild - '${DRONE_BUILD_NUMBER}'\n\nCommit - https://github.com/DarkFlippers/unleashed-firmware/commit/'${DRONE_COMMIT_SHA}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[-Version with Extra apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'e.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'e)\n\n[-Version with only main apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'c.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'c)\n\n[-Version with RGB patch - only for hardware mod! - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'r.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'r)\n\n[-Version with RGB patch - only for hardware mod! - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'r.tgz)\n\n[-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}')'
trigger:
branch:

1
.gitignore vendored
View file

@ -51,6 +51,7 @@ null.d
/artifacts-ofw-anims
/artifacts-rgb-patch
/artifacts-extra-apps
/artifacts-clean
# SCons build dir
/build

10
.gitmodules vendored
View file

@ -10,6 +10,7 @@
[submodule "assets/protobuf"]
path = assets/protobuf
url = https://github.com/flipperdevices/flipperzero-protobuf.git
shallow = false
[submodule "lib/libusb_stm32"]
path = lib/libusb_stm32
url = https://github.com/flipperdevices/libusb_stm32.git
@ -25,12 +26,6 @@
[submodule "lib/cxxheaderparser"]
path = lib/cxxheaderparser
url = https://github.com/robotpy/cxxheaderparser.git
[submodule "applications/external/dap_link/lib/free-dap"]
path = applications/external/dap_link/lib/free-dap
url = https://github.com/ataradov/free-dap.git
[submodule "applications/external/subbrute"]
path = applications/external/subbrute
url = https://github.com/DarkFlippers/flipperzero-subbrute.git
[submodule "lib/heatshrink"]
path = lib/heatshrink
url = https://github.com/flipperdevices/heatshrink.git
@ -43,6 +38,3 @@
[submodule "lib/stm32wb_copro"]
path = lib/stm32wb_copro
url = https://github.com/flipperdevices/stm32wb_copro.git
[submodule "applications/external/multi_fuzzer"]
path = applications/external/multi_fuzzer
url = https://github.com/DarkFlippers/Multi_Fuzzer.git

View file

@ -1 +1 @@
--ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/cmsis_core -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/stm32wb_cmsis -e lib/stm32wb_copro -e lib/stm32wb_hal -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/* -e applications/external/dap_link/lib/free-dap
--ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/cmsis_core -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/stm32wb_cmsis -e lib/stm32wb_copro -e lib/stm32wb_hal -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/*

9
.vscode/.gitignore vendored
View file

@ -1,4 +1,5 @@
./c_cpp_properties.json
./launch.json
./settings.json
./tasks.json
/c_cpp_properties.json
/extensions.json
/launch.json
/settings.json
/tasks.json

19
.vscode/example/clangd/extensions.json vendored Normal file
View file

@ -0,0 +1,19 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
// List of extensions which should be recommended for users of this workspace.
"recommendations": [
"ms-python.black-formatter",
"llvm-vs-code-extensions.vscode-clangd",
"amiralizadeh9480.cpp-helper",
"marus25.cortex-debug",
"zxh404.vscode-proto3",
"augustocdias.tasks-shell-input"
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": [
"twxs.cmake",
"ms-vscode.cpptools",
"ms-vscode.cmake-tools"
]
}

View file

@ -13,6 +13,7 @@
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": [
"llvm-vs-code-extensions.vscode-clangd",
"twxs.cmake",
"ms-vscode.cmake-tools"
]

View file

@ -21,5 +21,10 @@
"SConscript": "python",
"SConstruct": "python",
"*.fam": "python",
}
},
"clangd.arguments": [
// We might be able to tighten this a bit more to only include the correct toolchain.
"--query-driver=**",
"--compile-commands-dir=${workspaceFolder}/build/latest"
]
}

View file

@ -1,25 +1,31 @@
## New changes
* SubGHz: Support for Ebyte E07 module power amp switch (works with TehRabbitt's Flux Capacitor Board) (by @Sil333033) (PR #559 by @Z3BRO) -> Remade by @xMasterX -> Driver code fixed and reworked by @gid9798
* Infrared: Update universal remote assets (by @amec0e | PR #570)
* Infrared: Update universal AC asset (by @Leptopt1los | PR #569)
* Plugins: Add * in NFC Maker keyboard (hold `.`)
* Plugins: Update TOTP (Authenticator) [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator)
* Plugins: Update ESP32: WiFi Marauder companion plugin [(by 0xchocolate)](https://github.com/0xchocolate/flipperzero-wifi-marauder)
* Plugins: Update ESP32-CAM -> Camera Suite [(by CodyTolene)](https://github.com/CodyTolene/Flipper-Zero-Camera-Suite) -> (PR #562 by @CodyTolene)
* OFW PR 2949: IR: buttons move feature rework (by nminaylov)
* OFW PR 2941: FDX-B temperature now uses system units (by Astrrra)
* OFW: fbtenv: add additional environ variable to control execution flow
* OFW: NFC CLI: Fix multiple apdu commands from not working when one of them gives an empty response
* OFW: NFC: Fix MFC key invalidation
* OFW: Rename Applications to Apps
* OFW: Fix about screen
* OFW: change FuriThreadPriorityIsr to 31 (configMAX_PRIORITIES-1)
* OFW: External apps icounter
* OFW: Overly missed feature: Infrared: move button (change button order in a remote)
* OFW: Move U2F path to ext
* OFW: New RTC flags in device info
* OFW: Backlight notification fix
* OFW: Fix fbtenv restore
* NFC: Add manually MF Classic with custom UID (by @gid9798 | PR #571)
* Infrared: Fix crash when frequency is outside of supported range, allow sending broken files by setting frequency to nearest border
* Infrared: Updated AC universal asset (Legion LE-F30RH-IN added and other changes) (by @Leptopt1los | PR #577)
* SubGHz: Fix Somfy Telis wrong frequency in add manually
* SubGHz: Fix frequency out of bounds check causing crash/freeze
* RGB Patch: Fix colors
* Builds: New build (clean) `c` - comes only with main apps, use in case you don't want preinstalled apps pack
* Plugins: Moved into one repo - https://github.com/xMasterX/all-the-plugins
* OFW PR 2984: (only buffer changes was merged, other will be later) SubGhz: fix todo (by Skorpionm)
* OFW PR 2980: Properly reset the NFC device data (by Astrrra)
* OFW: SubGhz: add timeout to subghz_hal_async_tx_test_run
* OFW: Improve vscode clangd experience
* OFW: Add the Sad song animation
* OFW: ufbt: fixed FAP_SRC_DIR
* OFW: UI: Clock on Desktop -> Refactoring of our desktop clock
* OFW: uFBT: devboard_flash to update WiFi devboard
* OFW: FBT: devboard_flash to update WiFi devboard
* OFW: Scripts: OB recovery
* OFW: Expose additional functions of the crypto engine to user -> **Breaking API change 34.x -> 35.x**
* OFW: External apps removed -> In our case - moved into extra plugins repo to separate plugins and firmware
* OFW: BadUSB: qFlipper install script for MacOS
* OFW: Add compressor.h to the SDK
* OFW: fbt: Fix building using path with space
* OFW: RPC: md5 in storage list
* OFW: Fixes 2957 - subghz decode_raw
* OFW: FDX-B temperature in system units
* OFW: Infrared: buttons move feature rework
----
@ -47,12 +53,14 @@ and all other great people who supported our project and me (xMasterX), thanks t
## **Recommended update option - Web Updater**
### What `n`, `r`, `e` means? What I need to download if I don't want to use Web updater?
What build I should download and what this name means - `flipper-z-f7-update-(version)(n / r / e).tgz` ? <br>
### What `n`, `r`, `e`, ` `, `c` means? What I need to download if I don't want to use Web updater?
What build I should download and what this name means - `flipper-z-f7-update-(version)(n / r / e / c).tgz` ? <br>
`flipper-z` = for Flipper Zero device<br>
`f7` = Hardware version - same for all flipper zero devices<br>
`update` = Update package, contains updater, all assets (plugins, IR libs, etc.), and firmware itself<br>
`(version)` = Firmware version<br>
` ` = this build comes with 3 custom animations, and default apps preinstalled<br>
`c` = this build comes with 3 custom animations, and only main apps (Clean build)<br>
`n` = this build comes without our custom animations (we have only 3 of them), only official flipper animations<br>
`e` = build has 🎲 [extra apps pack](https://github.com/xMasterX/all-the-plugins) preinstalled<br>
`r` = RGB patch (+ extra apps) for flippers with rgb backlight mod (this is hardware mod!) (Works only on modded flippers!) (do not install on non modded device!)

166
ReadMe.md
View file

@ -32,43 +32,57 @@ Our Discord Community:
- https://t.me/unleashed_fw
# What's changed
* Sub-GHz regional TX restrictions removed
* Sub-GHz frequency range can be extended in settings file (Warning: It can damage Flipper's hardware)
* Many rolling code protocols now have the ability to save & send captured signals
* FAAC SLH (Spa) & BFT Mitto (keeloq secure with seed) manual creation
* Sub-GHz static code brute-force plugin
* LFRFID Fuzzer plugin
* Custom community plugins and games added + all known working apps can be downloaded in extra pack in every release
* Extra Sub-GHz frequencies + extra Mifare Classic keys
* Picopass/iClass plugin included in releases
* Recompiled IR TV Universal Remote for ALL buttons
* Universal remote for Projectors, Fans, A/Cs and Audio(soundbars, etc.)
* Customizable Flipper name **Update! Now can be changed in Settings->Desktop** (by @xMasterX and @Willy-JL)
* Text Input UI element -> Cursor feature (by @Willy-JL)
- **Sub-GHz** *lib & hal*
- regional TX restrictions removed
- Extra Sub-GHz frequencies
- frequency range can be extended in settings file (Warning: It can damage Flipper's hardware)
- Many rolling code [protocols](https://github.com/DarkFlippers/unleashed-firmware#current-modified-and-new-sub-ghz-protocols-list) now have the ability to save & send captured signals
- FAAC SLH (Spa) & BFT Mitto (keeloq secure with seed) manual creation
- External CC1101 module support [(by quen0n)](https://github.com/DarkFlippers/unleashed-firmware/pull/307)
- **Sub-GHz** *Main App*
- Save last used frequency [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/77)
- New frequency analyzer [(by ClusterM)](https://github.com/DarkFlippers/unleashed-firmware/pull/43)
- Press OK in frequency analyzer to use detected frequency in Read modes [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/77)
- Long press OK button in Sub-GHz Frequency analyzer to switch to Read menu [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/79)
- New option to use timestamps + protocol name when you saving file, instead of random name - Enable in `Radio Settings -> Time in names = ON`
- Read mode UI improvements (scrolling text, + shows time when signal was received) (by @wosk)
- External CC1101 module support (Hardware SPI used)
- **Hold right in received signal list to delete selected signal**
- **Custom buttons for Keeloq / Alutech AT4N / Nice Flor S / Somfy Telis / Security+ 2.0 / CAME Atomo** - now you can use arrow buttons to send signal with different button code
- `Add manually` menu extended with new protocols
- BFT Mitto / Somfy Telis / Nice Flor S / CAME Atomo, etc.. manual creation with programming new remote into receiver (use button 0xF for BFT Mitto, 0x8 (Prog) on Somfy Telis)
- Debug mode counter increase settings (+1 -> +5, +10, default: +1)
- Debug PIN output settings for protocol development
- **Sub-GHz apps** *by unleashed team*
- Sub-GHz Bruteforce - static code brute-force plugin |
- Time delay (between signals) setting (hold Up in main screen(says Up to Save)) + configure repeats in protocols list by pressing right button on selected protocol
- Load your own file and select bytes you want to bruteforce or use preconfigured options in protocols list
- Sub-GHz Remote - remote control for 5 sub-ghz files | bind one file for each button
- use the built-in constructor or make config file by following this [instruction](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemotePlugin.md)
- **Infrared**
- Recompiled IR TV Universal Remote for ALL buttons
- Universal remotes for Projectors, Fans, A/Cs and Audio(soundbars, etc.) -> Also always updated and verified by our team
- Infrared -> `RCA` Protocol
- Infrared -> Debug TX PIN output settings
- **NFC/RFID/iButton**
* LFRFID/iButton Fuzzer plugins
* Extra Mifare Classic keys
* `Add manually` -> Mifare Classic with custom UID
* Picopass/iClass plugin (now with emulation support!) included in releases
- **Quality of life & other features**
- Customizable Flipper name **Update! Now can be changed in Settings->Desktop** (by @xMasterX and @Willy-JL)
- Text Input UI element -> Cursor feature (by @Willy-JL)
- Byte Input Mini editor -> **Press UP** multiple times until the nibble editor appears
- Clock on Desktop -> `Settings -> Desktop -> Show Clock`
- Battery percentage display with different styles `Settings -> Desktop -> Battery View`
- More games in Dummy Mode -> click or hold any of arrow buttons
- Lock device with pin(or regular lock if pin not set) by holding UP button on main screen [(by an4tur0r)](https://github.com/DarkFlippers/unleashed-firmware/pull/107)
- **BadBT** plugin (BT version of BadKB) [(by Willy-JL, ClaraCrazy, XFW contributors)](https://github.com/ClaraCrazy/Flipper-Xtreme/tree/dev/applications/main/bad_kb) - (See in Applications->Tools) - (aka BadUSB via Bluetooth)
- BadUSB -> Keyboard layouts [(by rien > dummy-decoy)](https://github.com/dummy-decoy/flipperzero-firmware/tree/dummy_decoy/bad_usb_keyboard_layout)
- Sub-GHz -> External CC1101 module support - [(by quen0n)](https://github.com/DarkFlippers/unleashed-firmware/pull/307)
- Sub-GHz -> `Add manually` menu extended with new protocols
- Sub-GHz -> New frequency analyzer - [(by ClusterM)](https://github.com/DarkFlippers/unleashed-firmware/pull/43)
- Sub-GHz -> Save last used frequency [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/77)
- Sub-GHz -> Press OK in frequency analyzer to use detected frequency in Read modes [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/77)
- Sub-GHz -> Long press OK button in Sub-GHz Frequency analyzer to switch to Read menu [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/79)
- Lock device with pin(or regular lock if pin not set) by holding UP button on main screen [(by an4tur0r)](https://github.com/DarkFlippers/unleashed-firmware/pull/107)
* Sub-GHz -> Short press OK in frequency analyzer to save detected frequency for usage in Read modes
* Sub-GHz -> Long press OK button in Sub-GHz Frequency analyzer to switch to Read menu and automatically use selected frequency
* SubGHz -> New option to use timestamps + protocol name when you saving file, instead of random name - Enable in `Radio Settings -> Time in names = ON`
* SubGHz Bruteforcer plugin -> Time delay (between signals) setting (hold Up in main screen(says Up to Save)) + configure repeats in protocols list by pressing right button on selected protocol
* SubGHz -> Read mode UI improvements (scrolling text, + shows time when signal was received) (by @wosk)
* Sub-GHz -> External CC1101 module support (Hardware SPI used)
* SubGHz -> **Hold right in received signal list to delete selected signal**
* SubGHz -> **Custom buttons for Keeloq / Alutech AT4N / Nice Flor S / Somfy Telis / Security+ 2.0 / CAME Atomo** - now you can use arrow buttons to send signal with different button code
* SubGHz -> BFT Mitto / Somfy Telis / Nice Flor S / CAME Atomo, etc.. manual creation with programming new remote into receiver (use button 0xF for BFT Mitto, 0x8 (Prog) on Somfy Telis)
* SubGHz -> Debug mode counter increase settings (+1 -> +5, +10, default: +1)
* SubGHz -> Debug PIN output settings for protocol development
* Infrared -> `RCA` Protocol
* Infrared -> Debug TX PIN output settings
* Other small fixes and changes throughout
* See other changes in readme below
- Custom community plugins and games added + all known working apps can be downloaded in extra pack in every release
- Other small fixes and changes throughout
- See other changes in readme below
Also check the changelog in releases for latest updates!
@ -124,71 +138,23 @@ Our team is small and the guys are working on this project as much as they can s
The amount of work done on this project is huge and we need your support, no matter how large or small. Even if you just say, "Thank you Unleashed firmware developers!" somewhere. Doing so will help us continue our work and will help drive us to make the firmware better every time.
Also, regarding our releases, every build has and always will be free and open-source. There will be no paywall releases or closed-source apps within the firmware. As long as I am working on this project it will never happen.
You can support us by using links or addresses below:
* **Boosty** (patreon alternative): https://boosty.to/mmxdev
* cloudtips (only RU payments accepted): https://pay.cloudtips.ru/p/7b3e9d65
* YooMoney (only RU payments accepted): https://yoomoney.ru/fundraise/XA49mgQLPA0.221209
* USDT(TRC20): `TSXcitMSnWXUFqiUfEXrTVpVewXy2cYhrs`
* BCH: `qquxfyzntuqufy2dx0hrfr4sndp0tucvky4sw8qyu3`
* ETH/BSC/ERC20-Tokens: `darkflippers.eth` (or `0xFebF1bBc8229418FF2408C07AF6Afa49152fEc6a`)
* BTC: `bc1q0np836jk9jwr4dd7p6qv66d04vamtqkxrecck9`
* DOGE: `D6R6gYgBn5LwTNmPyvAQR6bZ9EtGgFCpvv`
* LTC: `ltc1q3ex4ejkl0xpx3znwrmth4lyuadr5qgv8tmq8z9`
* XMR (Monero): `41xUz92suUu1u5Mu4qkrcs52gtfpu9rnZRdBpCJ244KRHf6xXSvVFevdf2cnjS7RAeYr5hn9MsEfxKoFDRSctFjG5fv1Mhn`
* TON: `EQCOqcnYkvzOZUV_9bPE_8oTbOrOF03MnF-VcJyjisTZmpGf`
|Service|Remark|Link/Wallet|
|-|-|-|
|**Boosty**|patreon alternative|https://boosty.to/mmxdev|
|cloudtips|only RU payments accepted|https://pay.cloudtips.ru/p/7b3e9d65|
|YooMoney|only RU payments accepted|https://yoomoney.ru/fundraise/XA49mgQLPA0.221209|
|USDT|(TRC20)|`TSXcitMSnWXUFqiUfEXrTVpVewXy2cYhrs`|
|BCH||`qquxfyzntuqufy2dx0hrfr4sndp0tucvky4sw8qyu3`|
|ETH|(BSC/ERC20-Tokens)|`darkflippers.eth` (or `0xFebF1bBc8229418FF2408C07AF6Afa49152fEc6a`)|
|BTC||`bc1q0np836jk9jwr4dd7p6qv66d04vamtqkxrecck9`|
|DOGE||`D6R6gYgBn5LwTNmPyvAQR6bZ9EtGgFCpvv`|
|LTC||`ltc1q3ex4ejkl0xpx3znwrmth4lyuadr5qgv8tmq8z9`|
|XMR|(Monero)| `41xUz92suUu1u5Mu4qkrcs52gtfpu9rnZRdBpCJ244KRHf6xXSvVFevdf2cnjS7RAeYr5hn9MsEfxKoFDRSctFjG5fv1Mhn`|
|TON||`EQCOqcnYkvzOZUV_9bPE_8oTbOrOF03MnF-VcJyjisTZmpGf`|
### Community apps included:
- **RFID Fuzzer** [(by @gid9798)](https://github.com/DarkFlippers/Multi_Fuzzer) (original by Ganapati & xMasterX)
- **iButton Fuzzer** [(by @gid9798)](https://github.com/DarkFlippers/Multi_Fuzzer) (original by xMasterX)
- **Sub-GHz bruteforcer** [(by @derskythe & xMasterX)](https://github.com/derskythe/flipperzero-subbrute) [(original by Ganapati & xMasterX)](https://github.com/DarkFlippers/unleashed-firmware/pull/57)
- **Sub-GHz playlist** [(by darmiel)](https://github.com/DarkFlippers/unleashed-firmware/pull/62)
- ESP8266 Deauther plugin [(by SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-Wifi-ESP8266-Deauther-Module)
- WiFi Scanner plugin [(by SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-WiFi-Scanner_Module)
- MultiConverter plugin [(by theisolinearchip)](https://github.com/theisolinearchip/flipperzero_stuff)
- WAV Player [(OFW: DrZlo13)](https://github.com/flipperdevices/flipperzero-firmware/tree/zlo/wav-player) - Fixed and improved by [LTVA1](https://github.com/LTVA1/wav_player) -> Also outputs audio on `PA6` - `3(A6)` pin
- Barcode Generator [(by Kingal1337)](https://github.com/Kingal1337/flipper-barcode-generator)
- GPIO: Sentry Safe plugin [(by H4ckd4ddy)](https://github.com/H4ckd4ddy/flipperzero-sentry-safe-plugin)
- ESP32: WiFi Marauder companion plugin [(by 0xchocolate)](https://github.com/0xchocolate/flipperzero-wifi-marauder) - Saving .pcap on flipper microSD [by tcpassos](https://github.com/tcpassos/flipperzero-firmware-with-wifi-marauder-companion) -> Only with custom marauder build (It is necessary to uncomment "#define WRITE_PACKETS_SERIAL" in configs.h (in marauder fw) and compile the firmware for the wifi board.) Or download precompiled build -> [Download esp32_marauder_ver_flipper_sd_serial.bin](https://github.com/justcallmekoko/ESP32Marauder/releases/latest)
- NRF24: Sniffer & MouseJacker (with changes) [(by mothball187)](https://github.com/mothball187/flipperzero-nrf24/tree/main/mousejacker)
- Simple Clock (timer by GMMan) [(original by CompaqDisc)](https://gist.github.com/CompaqDisc/4e329c501bd03c1e801849b81f48ea61)
- **Sub-GHz Remote** [(by @gid9798)](https://github.com/gid9798)
- Spectrum Analyzer (with changes) [(by jolcese)](https://github.com/jolcese/flipperzero-firmware/tree/spectrum/applications/spectrum_analyzer) - [Ultra Narrow mode & scan channels non-consecutively](https://github.com/theY4Kman/flipperzero-firmware/commits?author=theY4Kman)
- Metronome [(by panki27)](https://github.com/panki27/Metronome)
- DTMF Dolphin [(by litui)](https://github.com/litui/dtmf_dolphin)
- **TOTP (Authenticator)** [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator)
- GPS [(by ezod)](https://github.com/ezod/flipperzero-gps) works with module `NMEA 0183` via UART (13TX, 14RX, GND pins on Flipper)
- i2c Tools [(by NaejEL)](https://github.com/NaejEL/flipperzero-i2ctools) - C0 -> SCL / C1 -> SDA / GND -> GND | 3v3 logic levels only!
- HC-SR04 Distance sensor - Ported and modified by @xMasterX [(original by Sanqui)](https://github.com/Sanqui/flipperzero-firmware/tree/hc_sr04) - How to connect -> (5V -> VCC) / (GND -> GND) / (13|TX -> Trig) / (14|RX -> Echo)
- Morse Code [(by wh00hw)](https://github.com/wh00hw/MorseCodeFAP)
- **Unitemp - Temperature sensors reader** (DHT11/22, DS18B20, BMP280, HTU21x and more) [(by quen0n)](https://github.com/quen0n/unitemp-flipperzero)
- Lightmeter [(by oleksiikutuzov)](https://github.com/oleksiikutuzov/flipperzero-lightmeter)
- HEX Viewer [(by QtRoS)](https://github.com/QtRoS/flipper-zero-hex-viewer)
- POCSAG Pager [(by xMasterX & Shmuma)](https://github.com/xMasterX/flipper-pager)
- Text Viewer [(by kowalski7cc & kyhwana)](https://github.com/kowalski7cc/flipper-zero-text-viewer/tree/refactor-text-app)
- **UART Terminal** [(by cool4uma)](https://github.com/cool4uma/UART_Terminal/tree/main)
- **ProtoView** [(by antirez)](https://github.com/antirez/protoview)
- **SWD Probe** [(by g3gg0)](https://github.com/g3gg0/flipper-swd_probe)
- IR Scope [(by kallanreed)](https://github.com/DarkFlippers/unleashed-firmware/pull/407)
- **BadBT** plugin (BT version of BadKB) [(by Willy-JL, ClaraCrazy, XFW contributors)](https://github.com/ClaraCrazy/Flipper-Xtreme/tree/dev/applications/main/bad_kb) (See in Applications->Tools) - (aka BadUSB via Bluetooth)
- **Mifare Nested** [(by AloneLiberty)](https://github.com/AloneLiberty/FlipperNested) - Works with PC and python app `FlipperNested`
- **NFC Maker** plugin (make tags with URLs, Wifi and other things) [(by Willy-JL)](https://github.com/ClaraCrazy/Flipper-Xtreme/tree/dev/applications/external/nfc_maker)
- ESP32-CAM -> Camera Suite [(by CodyTolene)](https://github.com/CodyTolene/Flipper-Zero-Camera-Suite)
Games:
- DOOM (ported and fixed by @xMasterX & @Svarich & @hedger) [(original by p4nic4ttack)](https://github.com/p4nic4ttack/doom-flipper-zero/)
- Zombiez [(Reworked By DevMilanIan)](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/pull/240) [(Original By Dooskington)](https://github.com/Dooskington/flipperzero-zombiez)
- Flappy Bird [(by DroomOne)](https://github.com/DroomOne/flipperzero-firmware/tree/dev/applications/flappy_bird)
- Arkanoid (refactored by xMasterX) [(by gotnull)](https://github.com/gotnull/flipperzero-firmware-wPlugins)
- Tic Tac Toe (refactored by xMasterX) [(by gotnull)](https://github.com/gotnull/flipperzero-firmware-wPlugins)
- Tetris (with fixes) [(by jeffplang)](https://github.com/jeffplang/flipperzero-firmware/tree/tetris_game/applications/tetris_game)
- Minesweeper [(by panki27)](https://github.com/panki27/minesweeper)
- Heap Defence (aka Stack Attack) - Ported to latest firmware by @xMasterX - [(original by wquinoa & Vedmein)](https://github.com/Vedmein/flipperzero-firmware/tree/hd/svisto-perdelki)
- Game15 [(by x27)](https://github.com/x27/flipperzero-game15)
- Solitaire [(by teeebor)](https://github.com/teeebor/flipper_games)
- BlackJack [(by teeebor)](https://github.com/teeebor/flipper_games)
- 2048 game [(by eugene-kirzhanov)](https://github.com/eugene-kirzhanov/flipper-zero-2048-game)
- Bomberduck [(by leo-need-more-coffee)](https://github.com/leo-need-more-coffee/flipperzero-bomberduck)
- JetPack Joyride [(by timstrasser)](https://github.com/timstrasser)
#### See full list and sources here: https://github.com/xMasterX/all-the-plugins/tree/dev
# Instructions
@ -234,9 +200,9 @@ Games:
## [- How to use: Unitemp - Temperature sensors reader](https://github.com/quen0n/unitemp-flipperzero#readme)
## [- How to use: [NMEA] GPS](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/applications/external/gps_nmea_uart/README.md)
## [- How to use: [NMEA] GPS](https://github.com/xMasterX/all-the-plugins/blob/dev/base_pack/gps_nmea_uart/README.md)
## [- How to use: i2c Tools](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/applications/external/flipper_i2ctools/README.md)
## [- How to use: i2c Tools](https://github.com/xMasterX/all-the-plugins/blob/dev/base_pack/flipper_i2ctools/README.md)
## [- How to use: [NRF24] plugins](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/NRF24.md)
@ -274,7 +240,7 @@ Games:
* CLion IDE - How to setup workspace for flipper firmware development: [https://krasovs.ky/2022/11/01/flipper-zero-clion.html](https://krasovs.ky/2022/11/01/flipper-zero-clion.html)
* Docs by atmanos / How to write your own app (outdated API): [https://flipper.atmanos.com/docs/overview/intro](https://flipper.atmanos.com/docs/overview/intro)
* Official Docs: [http://docs.flipperzero.one](http://docs.flipperzero.one)
* Official Docs: [docs.flipper.net](https://docs.flipper.net/)
* Official Forum: [forum.flipperzero.one](https://forum.flipperzero.one/)
# Project structure

View file

@ -45,6 +45,7 @@ distenv = coreenv.Clone(
],
ENV=os.environ,
UPDATE_BUNDLE_DIR="dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}",
VSCODE_LANG_SERVER=ARGUMENTS.get("LANG_SERVER", "cpptools"),
)
firmware_env = distenv.AddFwProject(
@ -327,6 +328,9 @@ distenv.PhonyTarget(
"cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py -p ${FLIP_PORT}"
)
# Update WiFi devboard firmware
distenv.PhonyTarget("devboard_flash", "${PYTHON3} ${FBT_SCRIPT_DIR}/wifi_board.py")
# Find blackmagic probe
distenv.PhonyTarget(
@ -345,7 +349,14 @@ distenv.PhonyTarget(
)
# Prepare vscode environment
vscode_dist = distenv.Install("#.vscode", distenv.Glob("#.vscode/example/*"))
VSCODE_LANG_SERVER = cmd_environment["LANG_SERVER"]
vscode_dist = distenv.Install(
"#.vscode",
[
distenv.Glob("#.vscode/example/*.json"),
distenv.Glob(f"#.vscode/example/{VSCODE_LANG_SERVER}/*.json"),
],
)
distenv.Precious(vscode_dist)
distenv.NoClean(vscode_dist)
distenv.Alias("vscode_dist", vscode_dist)

View file

@ -35,21 +35,6 @@ Applications for main Flipper menu.
- `u2f` - U2F Application
## External
External applications deployed to SD Card
- `clock` - Clock application
- `dap_link` - DAP Link OnChip debugger
- `hid_app` - USB/BT Remote controller
- `music_player` - Music player app (demo)
- `nfc_magic` - NFC MFC Magic card application
- `picopass` - Picopass reader / writer
- `signal_generator` - Signal generator app: PWM and clock generator
- `snake_game` - Snake game application
- `spi_mem_manager` - SPI Memory reader / flasher
- `weather_station` - SubGHz weather station
## services
Background services providing system APIs to applications.

View file

@ -3,6 +3,7 @@ App(
apptype=FlipperAppType.STARTUP,
entry_point="unit_tests_on_system_start",
cdefines=["APP_UNIT_TESTS"],
requires=["system_settings"],
provides=["delay_test"],
order=100,
)

View file

@ -0,0 +1,602 @@
#include <stdio.h>
#include <furi.h>
#include <furi_hal.h>
#include "../minunit.h"
static const uint8_t key_ctr_1[32] = {
0x77, 0x6B, 0xEF, 0xF2, 0x85, 0x1D, 0xB0, 0x6F, 0x4C, 0x8A, 0x05, 0x42, 0xC8, 0x69, 0x6F, 0x6C,
0x6A, 0x81, 0xAF, 0x1E, 0xEC, 0x96, 0xB4, 0xD3, 0x7F, 0xC1, 0xD6, 0x89, 0xE6, 0xC1, 0xC1, 0x04,
};
static const uint8_t iv_ctr_1[16] = {
0x00,
0x00,
0x00,
0x60,
0xDB,
0x56,
0x72,
0xC9,
0x7A,
0xA8,
0xF0,
0xB2,
0x00,
0x00,
0x00,
0x01,
};
static const uint8_t pt_ctr_1[16] = {
0x53,
0x69,
0x6E,
0x67,
0x6C,
0x65,
0x20,
0x62,
0x6C,
0x6F,
0x63,
0x6B,
0x20,
0x6D,
0x73,
0x67,
};
static const uint8_t tv_ctr_ct_1[16] = {
0x14,
0x5A,
0xD0,
0x1D,
0xBF,
0x82,
0x4E,
0xC7,
0x56,
0x08,
0x63,
0xDC,
0x71,
0xE3,
0xE0,
0xC0,
};
static const uint8_t key_ctr_2[32] = {
0x77, 0x6B, 0xEF, 0xF2, 0x85, 0x1D, 0xB0, 0x6F, 0x4C, 0x8A, 0x05, 0x42, 0xC8, 0x69, 0x6F, 0x6C,
0x6A, 0x81, 0xAF, 0x1E, 0xEC, 0x96, 0xB4, 0xD3, 0x7F, 0xC1, 0xD6, 0x89, 0xE6, 0xC1, 0xC1, 0x04,
};
static const uint8_t iv_ctr_2[16] = {
0x00,
0x00,
0x00,
0x60,
0xDB,
0x56,
0x72,
0xC9,
0x7A,
0xA8,
0xF0,
0xB2,
0x00,
0x00,
0x00,
0x01,
};
static const uint8_t pt_ctr_2[0] = {};
//static const uint8_t tv_ctr_ct_2[0] = {};
static const uint8_t key_ctr_3[32] = {
0xF6, 0xD6, 0x6D, 0x6B, 0xD5, 0x2D, 0x59, 0xBB, 0x07, 0x96, 0x36, 0x58, 0x79, 0xEF, 0xF8, 0x86,
0xC6, 0x6D, 0xD5, 0x1A, 0x5B, 0x6A, 0x99, 0x74, 0x4B, 0x50, 0x59, 0x0C, 0x87, 0xA2, 0x38, 0x84,
};
static const uint8_t iv_ctr_3[16] = {
0x00,
0xFA,
0xAC,
0x24,
0xC1,
0x58,
0x5E,
0xF1,
0x5A,
0x43,
0xD8,
0x75,
0x00,
0x00,
0x00,
0x01,
};
static const uint8_t pt_ctr_3[32] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
};
static const uint8_t tv_ctr_ct_3[32] = {
0xF0, 0x5E, 0x23, 0x1B, 0x38, 0x94, 0x61, 0x2C, 0x49, 0xEE, 0x00, 0x0B, 0x80, 0x4E, 0xB2, 0xA9,
0xB8, 0x30, 0x6B, 0x50, 0x8F, 0x83, 0x9D, 0x6A, 0x55, 0x30, 0x83, 0x1D, 0x93, 0x44, 0xAF, 0x1C,
};
static const uint8_t key_ctr_4[32] = {
0xFF, 0x7A, 0x61, 0x7C, 0xE6, 0x91, 0x48, 0xE4, 0xF1, 0x72, 0x6E, 0x2F, 0x43, 0x58, 0x1D, 0xE2,
0xAA, 0x62, 0xD9, 0xF8, 0x05, 0x53, 0x2E, 0xDF, 0xF1, 0xEE, 0xD6, 0x87, 0xFB, 0x54, 0x15, 0x3D,
};
static const uint8_t iv_ctr_4[16] = {
0x00,
0x1C,
0xC5,
0xB7,
0x51,
0xA5,
0x1D,
0x70,
0xA1,
0xC1,
0x11,
0x48,
0x00,
0x00,
0x00,
0x01,
};
static const uint8_t pt_ctr_4[36] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23,
};
static const uint8_t tv_ctr_ct_4[36] = {
0xEB, 0x6C, 0x52, 0x82, 0x1D, 0x0B, 0xBB, 0xF7, 0xCE, 0x75, 0x94, 0x46,
0x2A, 0xCA, 0x4F, 0xAA, 0xB4, 0x07, 0xDF, 0x86, 0x65, 0x69, 0xFD, 0x07,
0xF4, 0x8C, 0xC0, 0xB5, 0x83, 0xD6, 0x07, 0x1F, 0x1E, 0xC0, 0xE6, 0xB8,
};
static const uint8_t key_ctr_5[32] = {
0xFF, 0x7A, 0x61, 0x7C, 0xE6, 0x91, 0x48, 0xE4, 0xF1, 0x72, 0x6E, 0x2F, 0x43, 0x58, 0x1D, 0xE2,
0xAA, 0x62, 0xD9, 0xF8, 0x05, 0x53, 0x2E, 0xDF, 0xF1, 0xEE, 0xD6, 0x87, 0xFB, 0x54, 0x15, 0x3D,
};
static const uint8_t iv_ctr_5[16] = {
0x00,
0x1C,
0xC5,
0xB7,
0x51,
0xA5,
0x1D,
0x70,
0xA1,
0xC1,
0x11,
0x48,
0x00,
0x00,
0x00,
0x01,
};
static const uint8_t pt_ctr_5[0] = {};
//static const uint8_t tv_ctr_ct_5[0] = {};
static const uint8_t key_gcm_1[32] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const uint8_t iv_gcm_1[16] = {
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
};
static const uint8_t pt_gcm_1[0] = {};
//static const uint8_t tv_gcm_ct_1[0] = {};
static const uint8_t aad_gcm_1[0] = {};
static const uint8_t tv_gcm_tag_1[16] = {
0x53,
0x0F,
0x8A,
0xFB,
0xC7,
0x45,
0x36,
0xB9,
0xA9,
0x63,
0xB4,
0xF1,
0xC4,
0xCB,
0x73,
0x8B,
};
static const uint8_t key_gcm_2[32] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const uint8_t iv_gcm_2[16] = {
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
};
static const uint8_t pt_gcm_2[16] = {
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
};
static const uint8_t tv_gcm_ct_2[16] = {
0xCE,
0xA7,
0x40,
0x3D,
0x4D,
0x60,
0x6B,
0x6E,
0x07,
0x4E,
0xC5,
0xD3,
0xBA,
0xF3,
0x9D,
0x18,
};
static const uint8_t aad_gcm_2[0] = {};
static const uint8_t tv_gcm_tag_2[16] = {
0xD0,
0xD1,
0xC8,
0xA7,
0x99,
0x99,
0x6B,
0xF0,
0x26,
0x5B,
0x98,
0xB5,
0xD4,
0x8A,
0xB9,
0x19,
};
static const uint8_t key_gcm_3[32] = {
0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08,
0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08,
};
static const uint8_t iv_gcm_3[16] = {
0xCA,
0xFE,
0xBA,
0xBE,
0xFA,
0xCE,
0xDB,
0xAD,
0xDE,
0xCA,
0xF8,
0x88,
0x00,
0x00,
0x00,
0x00,
};
static const uint8_t pt_gcm_3[64] = {
0xD9, 0x31, 0x32, 0x25, 0xF8, 0x84, 0x06, 0xE5, 0xA5, 0x59, 0x09, 0xC5, 0xAF, 0xF5, 0x26, 0x9A,
0x86, 0xA7, 0xA9, 0x53, 0x15, 0x34, 0xF7, 0xDA, 0x2E, 0x4C, 0x30, 0x3D, 0x8A, 0x31, 0x8A, 0x72,
0x1C, 0x3C, 0x0C, 0x95, 0x95, 0x68, 0x09, 0x53, 0x2F, 0xCF, 0x0E, 0x24, 0x49, 0xA6, 0xB5, 0x25,
0xB1, 0x6A, 0xED, 0xF5, 0xAA, 0x0D, 0xE6, 0x57, 0xBA, 0x63, 0x7B, 0x39, 0x1A, 0xAF, 0xD2, 0x55,
};
static const uint8_t tv_gcm_ct_3[64] = {
0x52, 0x2D, 0xC1, 0xF0, 0x99, 0x56, 0x7D, 0x07, 0xF4, 0x7F, 0x37, 0xA3, 0x2A, 0x84, 0x42, 0x7D,
0x64, 0x3A, 0x8C, 0xDC, 0xBF, 0xE5, 0xC0, 0xC9, 0x75, 0x98, 0xA2, 0xBD, 0x25, 0x55, 0xD1, 0xAA,
0x8C, 0xB0, 0x8E, 0x48, 0x59, 0x0D, 0xBB, 0x3D, 0xA7, 0xB0, 0x8B, 0x10, 0x56, 0x82, 0x88, 0x38,
0xC5, 0xF6, 0x1E, 0x63, 0x93, 0xBA, 0x7A, 0x0A, 0xBC, 0xC9, 0xF6, 0x62, 0x89, 0x80, 0x15, 0xAD,
};
static const uint8_t aad_gcm_3[0] = {};
static const uint8_t tv_gcm_tag_3[16] = {
0xB0,
0x94,
0xDA,
0xC5,
0xD9,
0x34,
0x71,
0xBD,
0xEC,
0x1A,
0x50,
0x22,
0x70,
0xE3,
0xCC,
0x6C,
};
static const uint8_t key_gcm_4[32] = {
0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08,
0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08,
};
static const uint8_t iv_gcm_4[16] = {
0xCA,
0xFE,
0xBA,
0xBE,
0xFA,
0xCE,
0xDB,
0xAD,
0xDE,
0xCA,
0xF8,
0x88,
0x00,
0x00,
0x00,
0x00,
};
static const uint8_t pt_gcm_4[60] = {
0xD9, 0x31, 0x32, 0x25, 0xF8, 0x84, 0x06, 0xE5, 0xA5, 0x59, 0x09, 0xC5, 0xAF, 0xF5, 0x26,
0x9A, 0x86, 0xA7, 0xA9, 0x53, 0x15, 0x34, 0xF7, 0xDA, 0x2E, 0x4C, 0x30, 0x3D, 0x8A, 0x31,
0x8A, 0x72, 0x1C, 0x3C, 0x0C, 0x95, 0x95, 0x68, 0x09, 0x53, 0x2F, 0xCF, 0x0E, 0x24, 0x49,
0xA6, 0xB5, 0x25, 0xB1, 0x6A, 0xED, 0xF5, 0xAA, 0x0D, 0xE6, 0x57, 0xBA, 0x63, 0x7B, 0x39,
};
static const uint8_t tv_gcm_ct_4[60] = {
0x52, 0x2D, 0xC1, 0xF0, 0x99, 0x56, 0x7D, 0x07, 0xF4, 0x7F, 0x37, 0xA3, 0x2A, 0x84, 0x42,
0x7D, 0x64, 0x3A, 0x8C, 0xDC, 0xBF, 0xE5, 0xC0, 0xC9, 0x75, 0x98, 0xA2, 0xBD, 0x25, 0x55,
0xD1, 0xAA, 0x8C, 0xB0, 0x8E, 0x48, 0x59, 0x0D, 0xBB, 0x3D, 0xA7, 0xB0, 0x8B, 0x10, 0x56,
0x82, 0x88, 0x38, 0xC5, 0xF6, 0x1E, 0x63, 0x93, 0xBA, 0x7A, 0x0A, 0xBC, 0xC9, 0xF6, 0x62,
};
static const uint8_t aad_gcm_4[20] = {
0xFE, 0xED, 0xFA, 0xCE, 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED,
0xFA, 0xCE, 0xDE, 0xAD, 0xBE, 0xEF, 0xAB, 0xAD, 0xDA, 0xD2,
};
static const uint8_t tv_gcm_tag_4[16] = {
0x76,
0xFC,
0x6E,
0xCE,
0x0F,
0x4E,
0x17,
0x68,
0xCD,
0xDF,
0x88,
0x53,
0xBB,
0x2D,
0x55,
0x1B,
};
static void furi_hal_crypto_ctr_setup() {
}
static void furi_hal_crypto_ctr_teardown() {
}
static void furi_hal_crypto_gcm_setup() {
}
static void furi_hal_crypto_gcm_teardown() {
}
MU_TEST(furi_hal_crypto_ctr_1) {
bool ret = false;
uint8_t ct[sizeof(pt_ctr_1)];
ret = furi_hal_crypto_ctr(key_ctr_1, iv_ctr_1, pt_ctr_1, ct, sizeof(pt_ctr_1));
mu_assert(ret, "CTR 1 failed");
mu_assert_mem_eq(tv_ctr_ct_1, ct, sizeof(pt_ctr_1));
}
MU_TEST(furi_hal_crypto_ctr_2) {
bool ret = false;
uint8_t ct[sizeof(pt_ctr_2)];
ret = furi_hal_crypto_ctr(key_ctr_2, iv_ctr_2, pt_ctr_2, ct, sizeof(pt_ctr_2));
mu_assert(ret, "CTR 2 failed");
//mu_assert_mem_eq(tv_ctr_ct_2, ct, sizeof(pt_ctr_2));
}
MU_TEST(furi_hal_crypto_ctr_3) {
bool ret = false;
uint8_t ct[sizeof(pt_ctr_3)];
ret = furi_hal_crypto_ctr(key_ctr_3, iv_ctr_3, pt_ctr_3, ct, sizeof(pt_ctr_3));
mu_assert(ret, "CTR 3 failed");
mu_assert_mem_eq(tv_ctr_ct_3, ct, sizeof(pt_ctr_3));
}
MU_TEST(furi_hal_crypto_ctr_4) {
bool ret = false;
uint8_t ct[sizeof(pt_ctr_4)];
ret = furi_hal_crypto_ctr(key_ctr_4, iv_ctr_4, pt_ctr_4, ct, sizeof(pt_ctr_4));
mu_assert(ret, "CTR 4 failed");
mu_assert_mem_eq(tv_ctr_ct_4, ct, sizeof(pt_ctr_4));
}
MU_TEST(furi_hal_crypto_ctr_5) {
bool ret = false;
uint8_t ct[sizeof(pt_ctr_5)];
ret = furi_hal_crypto_ctr(key_ctr_5, iv_ctr_5, pt_ctr_5, ct, sizeof(pt_ctr_5));
mu_assert(ret, "CTR 5 failed");
//mu_assert_mem_eq(tv_ctr_ct_5, ct, sizeof(pt_ctr_5));
}
MU_TEST(furi_hal_crypto_gcm_1) {
bool ret = false;
uint8_t pt[sizeof(pt_gcm_1)];
uint8_t ct[sizeof(pt_gcm_1)];
uint8_t tag_enc[16];
uint8_t tag_dec[16];
ret = furi_hal_crypto_gcm(
key_gcm_1,
iv_gcm_1,
aad_gcm_1,
sizeof(aad_gcm_1),
pt_gcm_1,
ct,
sizeof(pt_gcm_1),
tag_enc,
false);
mu_assert(ret, "GCM 1 encryption failed");
//mu_assert_mem_eq(tv_gcm_ct_1, ct, sizeof(pt_gcm_1));
mu_assert_mem_eq(tv_gcm_tag_1, tag_enc, 16);
ret = furi_hal_crypto_gcm(
key_gcm_1, iv_gcm_1, aad_gcm_1, sizeof(aad_gcm_1), ct, pt, sizeof(pt_gcm_1), tag_dec, true);
mu_assert(ret, "GCM 1 decryption failed");
//mu_assert_mem_eq(pt_gcm_1, pt, sizeof(pt_gcm_1));
mu_assert_mem_eq(tv_gcm_tag_1, tag_dec, 16);
}
MU_TEST(furi_hal_crypto_gcm_2) {
bool ret = false;
uint8_t pt[sizeof(pt_gcm_2)];
uint8_t ct[sizeof(pt_gcm_2)];
uint8_t tag_enc[16];
uint8_t tag_dec[16];
ret = furi_hal_crypto_gcm(
key_gcm_2,
iv_gcm_2,
aad_gcm_2,
sizeof(aad_gcm_2),
pt_gcm_2,
ct,
sizeof(pt_gcm_2),
tag_enc,
false);
mu_assert(ret, "GCM 2 encryption failed");
mu_assert_mem_eq(tv_gcm_ct_2, ct, sizeof(pt_gcm_2));
mu_assert_mem_eq(tv_gcm_tag_2, tag_enc, 16);
ret = furi_hal_crypto_gcm(
key_gcm_2, iv_gcm_2, aad_gcm_2, sizeof(aad_gcm_2), ct, pt, sizeof(pt_gcm_2), tag_dec, true);
mu_assert(ret, "GCM 2 decryption failed");
mu_assert_mem_eq(pt_gcm_2, pt, sizeof(pt_gcm_2));
mu_assert_mem_eq(tv_gcm_tag_2, tag_dec, 16);
}
MU_TEST(furi_hal_crypto_gcm_3) {
bool ret = false;
uint8_t pt[sizeof(pt_gcm_3)];
uint8_t ct[sizeof(pt_gcm_3)];
uint8_t tag_enc[16];
uint8_t tag_dec[16];
ret = furi_hal_crypto_gcm(
key_gcm_3,
iv_gcm_3,
aad_gcm_3,
sizeof(aad_gcm_3),
pt_gcm_3,
ct,
sizeof(pt_gcm_3),
tag_enc,
false);
mu_assert(ret, "GCM 3 encryption failed");
mu_assert_mem_eq(tv_gcm_ct_3, ct, sizeof(pt_gcm_3));
mu_assert_mem_eq(tv_gcm_tag_3, tag_enc, 16);
ret = furi_hal_crypto_gcm(
key_gcm_3, iv_gcm_3, aad_gcm_3, sizeof(aad_gcm_3), ct, pt, sizeof(pt_gcm_3), tag_dec, true);
mu_assert(ret, "GCM 3 decryption failed");
mu_assert_mem_eq(pt_gcm_3, pt, sizeof(pt_gcm_3));
mu_assert_mem_eq(tv_gcm_tag_3, tag_dec, 16);
}
MU_TEST(furi_hal_crypto_gcm_4) {
bool ret = false;
uint8_t pt[sizeof(pt_gcm_4)];
uint8_t ct[sizeof(pt_gcm_4)];
uint8_t tag_enc[16];
uint8_t tag_dec[16];
ret = furi_hal_crypto_gcm(
key_gcm_4,
iv_gcm_4,
aad_gcm_4,
sizeof(aad_gcm_4),
pt_gcm_4,
ct,
sizeof(pt_gcm_4),
tag_enc,
false);
mu_assert(ret, "GCM 4 encryption failed");
mu_assert_mem_eq(tv_gcm_ct_4, ct, sizeof(pt_gcm_4));
mu_assert_mem_eq(tv_gcm_tag_4, tag_enc, 16);
ret = furi_hal_crypto_gcm(
key_gcm_4, iv_gcm_4, aad_gcm_4, sizeof(aad_gcm_4), ct, pt, sizeof(pt_gcm_4), tag_dec, true);
mu_assert(ret, "GCM 4 decryption failed");
mu_assert_mem_eq(pt_gcm_4, pt, sizeof(pt_gcm_4));
mu_assert_mem_eq(tv_gcm_tag_4, tag_dec, 16);
}
MU_TEST_SUITE(furi_hal_crypto_ctr_test) {
MU_SUITE_CONFIGURE(&furi_hal_crypto_ctr_setup, &furi_hal_crypto_ctr_teardown);
MU_RUN_TEST(furi_hal_crypto_ctr_1);
MU_RUN_TEST(furi_hal_crypto_ctr_2);
MU_RUN_TEST(furi_hal_crypto_ctr_3);
MU_RUN_TEST(furi_hal_crypto_ctr_4);
MU_RUN_TEST(furi_hal_crypto_ctr_5);
}
MU_TEST_SUITE(furi_hal_crypto_gcm_test) {
MU_SUITE_CONFIGURE(&furi_hal_crypto_gcm_setup, &furi_hal_crypto_gcm_teardown);
MU_RUN_TEST(furi_hal_crypto_gcm_1);
MU_RUN_TEST(furi_hal_crypto_gcm_2);
MU_RUN_TEST(furi_hal_crypto_gcm_3);
MU_RUN_TEST(furi_hal_crypto_gcm_4);
}
int run_minunit_test_furi_hal_crypto() {
MU_RUN_SUITE(furi_hal_crypto_ctr_test);
MU_RUN_SUITE(furi_hal_crypto_gcm_test);
return MU_EXIT_CODE;
}

View file

@ -13,7 +13,7 @@
#include <pb.h>
#include <pb_encode.h>
#include <m-list.h>
#include <lib/toolbox/md5.h>
#include <lib/toolbox/md5_calc.h>
#include <lib/toolbox/path.h>
#include <cli/cli.h>
#include <loader/loader.h>
@ -287,7 +287,8 @@ static void test_rpc_create_simple_message(
PB_Main* message,
uint16_t tag,
const char* str,
uint32_t command_id) {
uint32_t command_id,
bool flag) {
furi_check(message);
char* str_copy = NULL;
@ -308,6 +309,7 @@ static void test_rpc_create_simple_message(
break;
case PB_Main_storage_list_request_tag:
message->content.storage_list_request.path = str_copy;
message->content.storage_list_request.include_md5 = flag;
break;
case PB_Main_storage_mkdir_request_tag:
message->content.storage_mkdir_request.path = str_copy;
@ -419,6 +421,7 @@ static void
}
mu_check(result_msg_file->size == expected_msg_file->size);
mu_check(result_msg_file->type == expected_msg_file->type);
mu_assert_string_eq(expected_msg_file->md5sum, result_msg_file->md5sum);
if(result_msg_file->data && result_msg_file->type != PB_Storage_File_FileType_DIR) {
mu_check(!result_msg_file->data == !expected_msg_file->data); // Zlo: WTF???
@ -430,10 +433,10 @@ static void
}
static void test_rpc_compare_messages(PB_Main* result, PB_Main* expected) {
mu_check(result->command_id == expected->command_id);
mu_check(result->command_status == expected->command_status);
mu_check(result->has_next == expected->has_next);
mu_check(result->which_content == expected->which_content);
mu_assert_int_eq(expected->command_id, result->command_id);
mu_assert_int_eq(expected->command_status, result->command_status);
mu_assert_int_eq(expected->has_next, result->has_next);
mu_assert_int_eq(expected->which_content, result->which_content);
if(result->command_status != PB_CommandStatus_OK) {
mu_check(result->which_content == PB_Main_empty_tag);
}
@ -573,10 +576,15 @@ static void
static void test_rpc_storage_list_create_expected_list(
MsgList_t msg_list,
const char* path,
uint32_t command_id) {
uint32_t command_id,
bool append_md5) {
Storage* fs_api = furi_record_open(RECORD_STORAGE);
File* dir = storage_file_alloc(fs_api);
FuriString* md5 = furi_string_alloc();
FuriString* md5_path = furi_string_alloc();
File* file = storage_file_alloc(fs_api);
PB_Main response = {
.command_id = command_id,
.has_next = false,
@ -614,6 +622,17 @@ static void test_rpc_storage_list_create_expected_list(
list->file[i].data = NULL;
/* memory free inside rpc_encode_and_send() -> pb_release() */
list->file[i].name = name;
if(append_md5 && !file_info_is_dir(&fileinfo)) {
furi_string_printf(md5_path, "%s/%s", path, name);
if(md5_string_calc_file(file, furi_string_get_cstr(md5_path), md5, NULL)) {
char* md5sum = list->file[i].md5sum;
size_t md5sum_size = sizeof(list->file[i].md5sum);
snprintf(md5sum, md5sum_size, "%s", furi_string_get_cstr(md5));
}
}
++i;
}
} else {
@ -626,6 +645,10 @@ static void test_rpc_storage_list_create_expected_list(
response.has_next = false;
MsgList_push_back(msg_list, response);
furi_string_free(md5);
furi_string_free(md5_path);
storage_file_free(file);
storage_dir_close(dir);
storage_file_free(dir);
@ -675,16 +698,17 @@ static void test_rpc_free_msg_list(MsgList_t msg_list) {
MsgList_clear(msg_list);
}
static void test_rpc_storage_list_run(const char* path, uint32_t command_id) {
static void test_rpc_storage_list_run(const char* path, uint32_t command_id, bool md5) {
PB_Main request;
MsgList_t expected_msg_list;
MsgList_init(expected_msg_list);
test_rpc_create_simple_message(&request, PB_Main_storage_list_request_tag, path, command_id);
test_rpc_create_simple_message(
&request, PB_Main_storage_list_request_tag, path, command_id, md5);
if(!strcmp(path, "/")) {
test_rpc_storage_list_create_expected_list_root(expected_msg_list, command_id);
} else {
test_rpc_storage_list_create_expected_list(expected_msg_list, path, command_id);
test_rpc_storage_list_create_expected_list(expected_msg_list, path, command_id, md5);
}
test_rpc_encode_and_feed_one(&request, 0);
test_rpc_decode_and_compare(expected_msg_list, 0);
@ -694,15 +718,25 @@ static void test_rpc_storage_list_run(const char* path, uint32_t command_id) {
}
MU_TEST(test_storage_list) {
test_rpc_storage_list_run("/", ++command_id);
test_rpc_storage_list_run(EXT_PATH("nfc"), ++command_id);
test_rpc_storage_list_run("/", ++command_id, false);
test_rpc_storage_list_run(EXT_PATH("nfc"), ++command_id, false);
test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id, false);
test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id, false);
test_rpc_storage_list_run(EXT_PATH("infrared"), ++command_id, false);
test_rpc_storage_list_run(EXT_PATH("ibutton"), ++command_id, false);
test_rpc_storage_list_run(EXT_PATH("lfrfid"), ++command_id, false);
test_rpc_storage_list_run("error_path", ++command_id, false);
}
test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id);
test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id);
test_rpc_storage_list_run(EXT_PATH("infrared"), ++command_id);
test_rpc_storage_list_run(EXT_PATH("ibutton"), ++command_id);
test_rpc_storage_list_run(EXT_PATH("lfrfid"), ++command_id);
test_rpc_storage_list_run("error_path", ++command_id);
MU_TEST(test_storage_list_md5) {
test_rpc_storage_list_run("/", ++command_id, true);
test_rpc_storage_list_run(EXT_PATH("nfc"), ++command_id, true);
test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id, true);
test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id, true);
test_rpc_storage_list_run(EXT_PATH("infrared"), ++command_id, true);
test_rpc_storage_list_run(EXT_PATH("ibutton"), ++command_id, true);
test_rpc_storage_list_run(EXT_PATH("lfrfid"), ++command_id, true);
test_rpc_storage_list_run("error_path", ++command_id, true);
}
static void
@ -770,7 +804,8 @@ static void test_storage_read_run(const char* path, uint32_t command_id) {
MsgList_init(expected_msg_list);
test_rpc_add_read_to_list_by_reading_real_file(expected_msg_list, path, command_id);
test_rpc_create_simple_message(&request, PB_Main_storage_read_request_tag, path, command_id);
test_rpc_create_simple_message(
&request, PB_Main_storage_read_request_tag, path, command_id, false);
test_rpc_encode_and_feed_one(&request, 0);
test_rpc_decode_and_compare(expected_msg_list, 0);
@ -824,7 +859,8 @@ static void test_rpc_storage_info_run(const char* path, uint32_t command_id) {
MsgList_t expected_msg_list;
MsgList_init(expected_msg_list);
test_rpc_create_simple_message(&request, PB_Main_storage_info_request_tag, path, command_id);
test_rpc_create_simple_message(
&request, PB_Main_storage_info_request_tag, path, command_id, false);
PB_Main* response = MsgList_push_new(expected_msg_list);
response->command_id = command_id;
@ -856,7 +892,8 @@ static void test_rpc_storage_stat_run(const char* path, uint32_t command_id) {
MsgList_t expected_msg_list;
MsgList_init(expected_msg_list);
test_rpc_create_simple_message(&request, PB_Main_storage_stat_request_tag, path, command_id);
test_rpc_create_simple_message(
&request, PB_Main_storage_stat_request_tag, path, command_id, false);
Storage* fs_api = furi_record_open(RECORD_STORAGE);
FileInfo fileinfo;
@ -968,7 +1005,11 @@ static void test_storage_write_read_run(
test_rpc_add_empty_to_list(expected_msg_list, PB_CommandStatus_OK, *command_id);
test_rpc_create_simple_message(
MsgList_push_raw(input_msg_list), PB_Main_storage_read_request_tag, path, ++*command_id);
MsgList_push_raw(input_msg_list),
PB_Main_storage_read_request_tag,
path,
++*command_id,
false);
test_rpc_add_read_or_write_to_list(
expected_msg_list,
READ_RESPONSE,
@ -1041,7 +1082,8 @@ MU_TEST(test_storage_interrupt_continuous_same_system) {
MsgList_push_new(input_msg_list),
PB_Main_storage_mkdir_request_tag,
TEST_DIR "dir1",
command_id + 1);
command_id + 1,
false);
test_rpc_add_read_or_write_to_list(
input_msg_list,
WRITE_REQUEST,
@ -1121,7 +1163,8 @@ static void test_storage_delete_run(
MsgList_t expected_msg_list;
MsgList_init(expected_msg_list);
test_rpc_create_simple_message(&request, PB_Main_storage_delete_request_tag, path, command_id);
test_rpc_create_simple_message(
&request, PB_Main_storage_delete_request_tag, path, command_id, false);
request.content.storage_delete_request.recursive = recursive;
test_rpc_add_empty_to_list(expected_msg_list, status, command_id);
@ -1202,7 +1245,8 @@ static void test_storage_mkdir_run(const char* path, size_t command_id, PB_Comma
MsgList_t expected_msg_list;
MsgList_init(expected_msg_list);
test_rpc_create_simple_message(&request, PB_Main_storage_mkdir_request_tag, path, command_id);
test_rpc_create_simple_message(
&request, PB_Main_storage_mkdir_request_tag, path, command_id, false);
test_rpc_add_empty_to_list(expected_msg_list, status, command_id);
test_rpc_encode_and_feed_one(&request, 0);
@ -1229,33 +1273,15 @@ MU_TEST(test_storage_mkdir) {
static void test_storage_calculate_md5sum(const char* path, char* md5sum, size_t md5sum_size) {
Storage* api = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(api);
FuriString* md5 = furi_string_alloc();
if(storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) {
const uint16_t once_read_size = 512;
const uint8_t hash_size = MD5SUM_SIZE;
uint8_t* data = malloc(once_read_size);
uint8_t* hash = malloc(sizeof(uint8_t) * hash_size);
md5_context* md5_ctx = malloc(sizeof(md5_context));
md5_starts(md5_ctx);
while(true) {
uint16_t read_size = storage_file_read(file, data, once_read_size);
if(read_size == 0) break;
md5_update(md5_ctx, data, read_size);
}
md5_finish(md5_ctx, hash);
free(md5_ctx);
for(uint8_t i = 0; i < hash_size; i++) {
md5sum += snprintf(md5sum, md5sum_size, "%02x", hash[i]);
}
free(hash);
free(data);
if(md5_string_calc_file(file, path, md5, NULL)) {
snprintf(md5sum, md5sum_size, "%s", furi_string_get_cstr(md5));
} else {
furi_check(0);
}
furi_string_free(md5);
storage_file_close(file);
storage_file_free(file);
@ -1271,11 +1297,12 @@ static void test_storage_md5sum_run(
MsgList_t expected_msg_list;
MsgList_init(expected_msg_list);
test_rpc_create_simple_message(&request, PB_Main_storage_md5sum_request_tag, path, command_id);
test_rpc_create_simple_message(
&request, PB_Main_storage_md5sum_request_tag, path, command_id, false);
if(status == PB_CommandStatus_OK) {
PB_Main* response = MsgList_push_new(expected_msg_list);
test_rpc_create_simple_message(
response, PB_Main_storage_md5sum_response_tag, md5sum, command_id);
response, PB_Main_storage_md5sum_response_tag, md5sum, command_id, false);
response->command_status = status;
} else {
test_rpc_add_empty_to_list(expected_msg_list, status, command_id);
@ -1433,6 +1460,7 @@ MU_TEST_SUITE(test_rpc_storage) {
MU_RUN_TEST(test_storage_info);
MU_RUN_TEST(test_storage_stat);
MU_RUN_TEST(test_storage_list);
MU_RUN_TEST(test_storage_list_md5);
MU_RUN_TEST(test_storage_read);
MU_RUN_TEST(test_storage_write_read);
MU_RUN_TEST(test_storage_write);
@ -1731,7 +1759,8 @@ MU_TEST(test_rpc_multisession_storage) {
MsgList_push_raw(input_0),
PB_Main_storage_read_request_tag,
TEST_DIR "file0.txt",
++command_id);
++command_id,
false);
test_rpc_add_read_or_write_to_list(
expected_0, READ_RESPONSE, TEST_DIR "file0.txt", pattern, sizeof(pattern), 1, command_id);
@ -1739,7 +1768,8 @@ MU_TEST(test_rpc_multisession_storage) {
MsgList_push_raw(input_1),
PB_Main_storage_read_request_tag,
TEST_DIR "file1.txt",
++command_id);
++command_id,
false);
test_rpc_add_read_or_write_to_list(
expected_1, READ_RESPONSE, TEST_DIR "file1.txt", pattern, sizeof(pattern), 1, command_id);

View file

@ -582,6 +582,49 @@ MU_TEST(test_storage_common_migrate) {
furi_record_close(RECORD_STORAGE);
}
#define MD5_HASH_SIZE (16)
#include <lib/toolbox/md5_calc.h>
MU_TEST(test_md5_calc) {
Storage* storage = furi_record_open(RECORD_STORAGE);
File* file = storage_file_alloc(storage);
const char* path = UNIT_TESTS_PATH("storage/md5.txt");
const char* md5_cstr = "2a456fa43e75088fdde41c93159d62a2";
const uint8_t md5[MD5_HASH_SIZE] = {
0x2a,
0x45,
0x6f,
0xa4,
0x3e,
0x75,
0x08,
0x8f,
0xdd,
0xe4,
0x1c,
0x93,
0x15,
0x9d,
0x62,
0xa2,
};
uint8_t md5_output[MD5_HASH_SIZE];
FuriString* md5_output_str = furi_string_alloc();
memset(md5_output, 0, MD5_HASH_SIZE);
mu_check(md5_calc_file(file, path, md5_output, NULL));
mu_check(md5_string_calc_file(file, path, md5_output_str, NULL));
mu_assert_mem_eq(md5, md5_output, MD5_HASH_SIZE);
mu_assert_string_eq(md5_cstr, furi_string_get_cstr(md5_output_str));
storage_file_free(file);
furi_string_free(md5_output_str);
furi_record_close(RECORD_STORAGE);
}
MU_TEST_SUITE(test_data_path) {
MU_RUN_TEST(test_storage_data_path);
MU_RUN_TEST(test_storage_data_path_apps);
@ -591,11 +634,16 @@ MU_TEST_SUITE(test_storage_common) {
MU_RUN_TEST(test_storage_common_migrate);
}
MU_TEST_SUITE(test_md5_calc_suite) {
MU_RUN_TEST(test_md5_calc);
}
int run_minunit_test_storage() {
MU_RUN_SUITE(storage_file);
MU_RUN_SUITE(storage_dir);
MU_RUN_SUITE(storage_rename);
MU_RUN_SUITE(test_data_path);
MU_RUN_SUITE(test_storage_common);
MU_RUN_SUITE(test_md5_calc_suite);
return MU_EXIT_CODE;
}

View file

@ -330,7 +330,12 @@ bool subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestType type) {
return false;
}
FuriHalCortexTimer timer = furi_hal_cortex_timer_get(30000000);
while(!furi_hal_subghz_is_async_tx_complete()) {
if(furi_hal_cortex_timer_is_expired(timer)) {
return false;
}
furi_delay_ms(10);
}
furi_hal_subghz_stop_async_tx();

View file

@ -10,6 +10,7 @@
int run_minunit_test_furi();
int run_minunit_test_furi_hal();
int run_minunit_test_furi_hal_crypto();
int run_minunit_test_furi_string();
int run_minunit_test_infrared();
int run_minunit_test_rpc();
@ -39,6 +40,7 @@ typedef struct {
const UnitTest unit_tests[] = {
{.name = "furi", .entry = run_minunit_test_furi},
{.name = "furi_hal", .entry = run_minunit_test_furi_hal},
{.name = "furi_hal_crypto", .entry = run_minunit_test_furi_hal_crypto},
{.name = "furi_string", .entry = run_minunit_test_furi_string},
{.name = "storage", .entry = run_minunit_test_storage},
{.name = "stream", .entry = run_minunit_test_stream},

View file

@ -7,6 +7,5 @@ App(
requires=["gui"],
stack_size=1 * 1024,
order=60,
fap_icon="../../external/mousejacker/mouse_10px.png",
fap_category="Debug",
)

View file

@ -357,7 +357,7 @@ bool subghz_device_cc1101_ext_rx_pipe_not_empty() {
(CC1101_STATUS_RXBYTES) | CC1101_BURST,
(uint8_t*)status);
furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);
// TODO: you can add a buffer overflow flag if needed
// TODO: Find reason why RXFIFO_OVERFLOW doesnt work correctly
if(status->NUM_RXBYTES > 0) {
return true;
} else {

View file

@ -1,6 +0,0 @@
# Placeholder
App(
appid="external_apps",
name="External apps bundle",
apptype=FlipperAppType.METAPACKAGE,
)

View file

@ -1,14 +0,0 @@
App(
appid="arkanoid",
name="Arkanoid",
apptype=FlipperAppType.EXTERNAL,
entry_point="arkanoid_game_app",
requires=["gui"],
stack_size=1 * 1024,
order=30,
fap_icon="arkanoid_10px.png",
fap_category="Games",
fap_author="@xMasterX & @gotnull",
fap_version="1.0",
fap_description="Arkanoid Game",
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -1,479 +0,0 @@
#include <furi.h>
#include <gui/gui.h>
#include <input/input.h>
#include <stdlib.h>
#include <gui/view.h>
#include <notification/notification.h>
#include <notification/notification_messages.h>
#include <dolphin/dolphin.h>
#define TAG "Arkanoid"
#define FLIPPER_LCD_WIDTH 128
#define FLIPPER_LCD_HEIGHT 64
#define MAX_SPEED 3
typedef enum { EventTypeTick, EventTypeKey } EventType;
typedef struct {
//Brick Bounds used in collision detection
int leftBrick;
int rightBrick;
int topBrick;
int bottomBrick;
bool isHit[4][13]; //Array of if bricks are hit or not
} BrickState;
typedef struct {
int dx; //Initial movement of ball
int dy; //Initial movement of ball
int xb; //Balls starting possition
int yb; //Balls starting possition
bool released; //If the ball has been released by the player
//Ball Bounds used in collision detection
int leftBall;
int rightBall;
int topBall;
int bottomBall;
} BallState;
typedef struct {
FuriMutex* mutex;
BallState ball_state;
BrickState brick_state;
NotificationApp* notify;
unsigned int COLUMNS; //Columns of bricks
unsigned int ROWS; //Rows of bricks
bool initialDraw; //If the inital draw has happened
int xPaddle; //X position of paddle
char text[16]; //General string buffer
bool bounced; //Used to fix double bounce glitch
int lives; //Amount of lives
int level; //Current level
unsigned int score; //Score for the game
unsigned int brickCount; //Amount of bricks hit
int tick; //Tick counter
bool gameStarted; // Did the game start?
int speed; // Ball speed
} ArkanoidState;
typedef struct {
EventType type;
InputEvent input;
} GameEvent;
static const NotificationSequence sequence_short_sound = {
&message_note_c5,
&message_delay_50,
&message_sound_off,
NULL,
};
// generate number in range [min,max)
int rand_range(int min, int max) {
return min + rand() % (max - min);
}
void move_ball(Canvas* canvas, ArkanoidState* st) {
st->tick++;
int current_speed = abs(st->speed - 1 - MAX_SPEED);
if(st->tick % current_speed != 0 && st->tick % (current_speed + 1) != 0) {
return;
}
if(st->ball_state.released) {
//Move ball
if(abs(st->ball_state.dx) == 2) {
st->ball_state.xb += st->ball_state.dx / 2;
// 2x speed is really 1.5 speed
if((st->tick / current_speed) % 2 == 0) st->ball_state.xb += st->ball_state.dx / 2;
} else {
st->ball_state.xb += st->ball_state.dx;
}
st->ball_state.yb = st->ball_state.yb + st->ball_state.dy;
//Set bounds
st->ball_state.leftBall = st->ball_state.xb;
st->ball_state.rightBall = st->ball_state.xb + 2;
st->ball_state.topBall = st->ball_state.yb;
st->ball_state.bottomBall = st->ball_state.yb + 2;
//Bounce off top edge
if(st->ball_state.yb <= 0) {
st->ball_state.yb = 2;
st->ball_state.dy = -st->ball_state.dy;
}
//Lose a life if bottom edge hit
if(st->ball_state.yb >= FLIPPER_LCD_HEIGHT) {
canvas_draw_frame(canvas, st->xPaddle, FLIPPER_LCD_HEIGHT - 1, 11, 1);
st->xPaddle = 54;
st->ball_state.yb = 60;
st->ball_state.released = false;
st->lives--;
st->gameStarted = false;
if(rand_range(0, 2) == 0) {
st->ball_state.dx = 1;
} else {
st->ball_state.dx = -1;
}
}
//Bounce off left side
if(st->ball_state.xb <= 0) {
st->ball_state.xb = 2;
st->ball_state.dx = -st->ball_state.dx;
}
//Bounce off right side
if(st->ball_state.xb >= FLIPPER_LCD_WIDTH - 2) {
st->ball_state.xb = FLIPPER_LCD_WIDTH - 4;
st->ball_state.dx = -st->ball_state.dx;
// arduboy.tunes.tone(523, 250);
}
//Bounce off paddle
if(st->ball_state.xb + 1 >= st->xPaddle && st->ball_state.xb <= st->xPaddle + 12 &&
st->ball_state.yb + 2 >= FLIPPER_LCD_HEIGHT - 1 &&
st->ball_state.yb <= FLIPPER_LCD_HEIGHT) {
st->ball_state.dy = -st->ball_state.dy;
st->ball_state.dx =
((st->ball_state.xb - (st->xPaddle + 6)) / 3); //Applies spin on the ball
// prevent straight bounce, but not prevent roguuemaster from stealing
if(st->ball_state.dx == 0) {
st->ball_state.dx = (rand_range(0, 2) == 1) ? 1 : -1;
}
}
//Bounce off Bricks
for(unsigned int row = 0; row < st->ROWS; row++) {
for(unsigned int column = 0; column < st->COLUMNS; column++) {
if(!st->brick_state.isHit[row][column]) {
//Sets Brick bounds
st->brick_state.leftBrick = 10 * column;
st->brick_state.rightBrick = 10 * column + 10;
st->brick_state.topBrick = 6 * row + 1;
st->brick_state.bottomBrick = 6 * row + 7;
//If A collison has occured
if(st->ball_state.topBall <= st->brick_state.bottomBrick &&
st->ball_state.bottomBall >= st->brick_state.topBrick &&
st->ball_state.leftBall <= st->brick_state.rightBrick &&
st->ball_state.rightBall >= st->brick_state.leftBrick) {
st->score += st->level;
// Blink led when we hit some brick
notification_message(st->notify, &sequence_short_sound);
//notification_message(st->notify, &sequence_blink_white_100);
st->brickCount++;
st->brick_state.isHit[row][column] = true;
canvas_draw_frame(canvas, 10 * column, 2 + 6 * row, 8, 4);
//Vertical collision
if(st->ball_state.bottomBall > st->brick_state.bottomBrick ||
st->ball_state.topBall < st->brick_state.topBrick) {
//Only bounce once each ball move
if(!st->bounced) {
st->ball_state.dy = -st->ball_state.dy;
st->ball_state.yb += st->ball_state.dy;
st->bounced = true;
}
}
//Hoizontal collision
if(st->ball_state.leftBall < st->brick_state.leftBrick ||
st->ball_state.rightBall > st->brick_state.rightBrick) {
//Only bounce once brick each ball move
if(!st->bounced) {
st->ball_state.dx = -st->ball_state.dx;
st->ball_state.xb += st->ball_state.dx;
st->bounced = true;
}
}
}
}
}
}
//Reset Bounce
st->bounced = false;
} else {
//Ball follows paddle
st->ball_state.xb = st->xPaddle + 5;
}
}
void draw_lives(Canvas* canvas, ArkanoidState* arkanoid_state) {
if(arkanoid_state->lives == 3) {
canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 7);
canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 7);
canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 8);
canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 8);
canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 11);
canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 11);
canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 12);
canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 12);
canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 15);
canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 15);
canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 16);
canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 16);
} else if(arkanoid_state->lives == 2) {
canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 7);
canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 7);
canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 8);
canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 8);
canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 11);
canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 11);
canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 12);
canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 12);
} else {
canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 7);
canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 7);
canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 8);
canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 8);
}
}
void draw_score(Canvas* canvas, ArkanoidState* arkanoid_state) {
snprintf(arkanoid_state->text, sizeof(arkanoid_state->text), "%u", arkanoid_state->score);
canvas_draw_str_aligned(
canvas,
FLIPPER_LCD_WIDTH - 2,
FLIPPER_LCD_HEIGHT - 6,
AlignRight,
AlignBottom,
arkanoid_state->text);
}
void draw_ball(Canvas* canvas, ArkanoidState* ast) {
canvas_draw_dot(canvas, ast->ball_state.xb, ast->ball_state.yb);
canvas_draw_dot(canvas, ast->ball_state.xb + 1, ast->ball_state.yb);
canvas_draw_dot(canvas, ast->ball_state.xb, ast->ball_state.yb + 1);
canvas_draw_dot(canvas, ast->ball_state.xb + 1, ast->ball_state.yb + 1);
move_ball(canvas, ast);
}
void draw_paddle(Canvas* canvas, ArkanoidState* arkanoid_state) {
canvas_draw_frame(canvas, arkanoid_state->xPaddle, FLIPPER_LCD_HEIGHT - 1, 11, 1);
}
void reset_level(Canvas* canvas, ArkanoidState* arkanoid_state) {
//Undraw paddle
canvas_draw_frame(canvas, arkanoid_state->xPaddle, FLIPPER_LCD_HEIGHT - 1, 11, 1);
//Undraw ball
canvas_draw_dot(canvas, arkanoid_state->ball_state.xb, arkanoid_state->ball_state.yb);
canvas_draw_dot(canvas, arkanoid_state->ball_state.xb + 1, arkanoid_state->ball_state.yb);
canvas_draw_dot(canvas, arkanoid_state->ball_state.xb, arkanoid_state->ball_state.yb + 1);
canvas_draw_dot(canvas, arkanoid_state->ball_state.xb + 1, arkanoid_state->ball_state.yb + 1);
//Alter various variables to reset the game
arkanoid_state->xPaddle = 54;
arkanoid_state->ball_state.yb = 60;
arkanoid_state->brickCount = 0;
arkanoid_state->ball_state.released = false;
arkanoid_state->gameStarted = false;
// Reset all brick hit states
for(unsigned int row = 0; row < arkanoid_state->ROWS; row++) {
for(unsigned int column = 0; column < arkanoid_state->COLUMNS; column++) {
arkanoid_state->brick_state.isHit[row][column] = false;
}
}
}
static void arkanoid_state_init(ArkanoidState* arkanoid_state) {
// Init notification
arkanoid_state->notify = furi_record_open(RECORD_NOTIFICATION);
// Set the initial game state
arkanoid_state->COLUMNS = 13;
arkanoid_state->ROWS = 4;
arkanoid_state->ball_state.dx = -1;
arkanoid_state->ball_state.dy = -1;
arkanoid_state->speed = 2;
arkanoid_state->bounced = false;
arkanoid_state->lives = 3;
arkanoid_state->level = 1;
arkanoid_state->score = 0;
arkanoid_state->COLUMNS = 13;
arkanoid_state->COLUMNS = 13;
// Reset initial state
arkanoid_state->initialDraw = false;
arkanoid_state->gameStarted = false;
}
static void arkanoid_draw_callback(Canvas* const canvas, void* ctx) {
furi_assert(ctx);
ArkanoidState* arkanoid_state = ctx;
furi_mutex_acquire(arkanoid_state->mutex, FuriWaitForever);
//Initial level draw
if(!arkanoid_state->initialDraw) {
arkanoid_state->initialDraw = true;
// Set default font for text
canvas_set_font(canvas, FontSecondary);
//Draws the new level
reset_level(canvas, arkanoid_state);
}
//Draws new bricks and resets their values
for(unsigned int row = 0; row < arkanoid_state->ROWS; row++) {
for(unsigned int column = 0; column < arkanoid_state->COLUMNS; column++) {
if(!arkanoid_state->brick_state.isHit[row][column]) {
canvas_draw_frame(canvas, 10 * column, 2 + 6 * row, 8, 4);
}
}
}
if(arkanoid_state->lives > 0) {
draw_paddle(canvas, arkanoid_state);
draw_ball(canvas, arkanoid_state);
draw_score(canvas, arkanoid_state);
draw_lives(canvas, arkanoid_state);
if(arkanoid_state->brickCount == arkanoid_state->ROWS * arkanoid_state->COLUMNS) {
arkanoid_state->level++;
reset_level(canvas, arkanoid_state);
}
} else {
reset_level(canvas, arkanoid_state);
arkanoid_state->initialDraw = false;
arkanoid_state->lives = 3;
arkanoid_state->score = 0;
}
furi_mutex_release(arkanoid_state->mutex);
}
static void arkanoid_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
furi_assert(event_queue);
GameEvent event = {.type = EventTypeKey, .input = *input_event};
furi_message_queue_put(event_queue, &event, FuriWaitForever);
}
static void arkanoid_update_timer_callback(FuriMessageQueue* event_queue) {
furi_assert(event_queue);
GameEvent event = {.type = EventTypeTick};
furi_message_queue_put(event_queue, &event, 0);
}
int32_t arkanoid_game_app(void* p) {
UNUSED(p);
int32_t return_code = 0;
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(GameEvent));
ArkanoidState* arkanoid_state = malloc(sizeof(ArkanoidState));
arkanoid_state_init(arkanoid_state);
arkanoid_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
if(!arkanoid_state->mutex) {
FURI_LOG_E(TAG, "Cannot create mutex\r\n");
return_code = 255;
goto free_and_exit;
}
// Set system callbacks
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, arkanoid_draw_callback, arkanoid_state);
view_port_input_callback_set(view_port, arkanoid_input_callback, event_queue);
FuriTimer* timer =
furi_timer_alloc(arkanoid_update_timer_callback, FuriTimerTypePeriodic, event_queue);
furi_timer_start(timer, furi_kernel_get_tick_frequency() / 22);
// Open GUI and register view_port
Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
// Call dolphin deed on game start
dolphin_deed(DolphinDeedPluginGameStart);
GameEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
furi_mutex_acquire(arkanoid_state->mutex, FuriWaitForever);
if(event_status == FuriStatusOk) {
// Key events
if(event.type == EventTypeKey) {
if(event.input.type == InputTypePress || event.input.type == InputTypeLong ||
event.input.type == InputTypeRepeat) {
switch(event.input.key) {
case InputKeyBack:
processing = false;
break;
case InputKeyRight:
if(arkanoid_state->xPaddle < FLIPPER_LCD_WIDTH - 12) {
arkanoid_state->xPaddle += 8;
}
break;
case InputKeyLeft:
if(arkanoid_state->xPaddle > 0) {
arkanoid_state->xPaddle -= 8;
}
break;
case InputKeyUp:
if(arkanoid_state->speed < MAX_SPEED) {
arkanoid_state->speed++;
}
break;
case InputKeyDown:
if(arkanoid_state->speed > 1) {
arkanoid_state->speed--;
}
break;
case InputKeyOk:
if(arkanoid_state->gameStarted == false) {
//Release ball if FIRE pressed
arkanoid_state->ball_state.released = true;
//Apply random direction to ball on release
if(rand_range(0, 2) == 0) {
arkanoid_state->ball_state.dx = 1;
} else {
arkanoid_state->ball_state.dx = -1;
}
//Makes sure the ball heads upwards
arkanoid_state->ball_state.dy = -1;
//start the game flag
arkanoid_state->gameStarted = true;
}
break;
default:
break;
}
}
}
}
view_port_update(view_port);
furi_mutex_release(arkanoid_state->mutex);
}
furi_timer_free(timer);
view_port_enabled_set(view_port, false);
gui_remove_view_port(gui, view_port);
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_NOTIFICATION);
view_port_free(view_port);
furi_mutex_free(arkanoid_state->mutex);
free_and_exit:
free(arkanoid_state);
furi_message_queue_free(event_queue);
return return_code;
}

View file

@ -1,17 +0,0 @@
App(
appid="avr_isp",
name="AVR Flasher",
apptype=FlipperAppType.EXTERNAL,
entry_point="avr_isp_app",
requires=["gui"],
stack_size=4 * 1024,
order=20,
fap_icon="avr_app_icon_10x10.png",
fap_category="GPIO",
fap_icon_assets="images",
fap_private_libs=[
Lib(
name="driver",
),
],
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

View file

@ -1,179 +0,0 @@
#include "avr_isp_app_i.h"
static bool avr_isp_app_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
AvrIspApp* app = context;
return scene_manager_handle_custom_event(app->scene_manager, event);
}
static bool avr_isp_app_back_event_callback(void* context) {
furi_assert(context);
AvrIspApp* app = context;
return scene_manager_handle_back_event(app->scene_manager);
}
static void avr_isp_app_tick_event_callback(void* context) {
furi_assert(context);
AvrIspApp* app = context;
scene_manager_handle_tick_event(app->scene_manager);
}
AvrIspApp* avr_isp_app_alloc() {
AvrIspApp* app = malloc(sizeof(AvrIspApp));
app->file_path = furi_string_alloc();
furi_string_set(app->file_path, STORAGE_APP_DATA_PATH_PREFIX);
app->error = AvrIspErrorNoError;
// GUI
app->gui = furi_record_open(RECORD_GUI);
// View Dispatcher
app->view_dispatcher = view_dispatcher_alloc();
app->scene_manager = scene_manager_alloc(&avr_isp_scene_handlers, app);
view_dispatcher_enable_queue(app->view_dispatcher);
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
view_dispatcher_set_custom_event_callback(
app->view_dispatcher, avr_isp_app_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, avr_isp_app_back_event_callback);
view_dispatcher_set_tick_event_callback(
app->view_dispatcher, avr_isp_app_tick_event_callback, 100);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
// Open Notification record
app->notifications = furi_record_open(RECORD_NOTIFICATION);
// SubMenu
app->submenu = submenu_alloc();
view_dispatcher_add_view(
app->view_dispatcher, AvrIspViewSubmenu, submenu_get_view(app->submenu));
// Widget
app->widget = widget_alloc();
view_dispatcher_add_view(app->view_dispatcher, AvrIspViewWidget, widget_get_view(app->widget));
// Text Input
app->text_input = text_input_alloc();
view_dispatcher_add_view(
app->view_dispatcher, AvrIspViewTextInput, text_input_get_view(app->text_input));
// Popup
app->popup = popup_alloc();
view_dispatcher_add_view(app->view_dispatcher, AvrIspViewPopup, popup_get_view(app->popup));
//Dialog
app->dialogs = furi_record_open(RECORD_DIALOGS);
// Programmer view
app->avr_isp_programmer_view = avr_isp_programmer_view_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
AvrIspViewProgrammer,
avr_isp_programmer_view_get_view(app->avr_isp_programmer_view));
// Reader view
app->avr_isp_reader_view = avr_isp_reader_view_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
AvrIspViewReader,
avr_isp_reader_view_get_view(app->avr_isp_reader_view));
// Writer view
app->avr_isp_writer_view = avr_isp_writer_view_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
AvrIspViewWriter,
avr_isp_writer_view_get_view(app->avr_isp_writer_view));
// Chip detect view
app->avr_isp_chip_detect_view = avr_isp_chip_detect_view_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
AvrIspViewChipDetect,
avr_isp_chip_detect_view_get_view(app->avr_isp_chip_detect_view));
// Enable 5v power, multiple attempts to avoid issues with power chip protection false triggering
uint8_t attempts = 0;
while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) {
furi_hal_power_enable_otg();
furi_delay_ms(10);
}
scene_manager_next_scene(app->scene_manager, AvrIspSceneStart);
return app;
} //-V773
void avr_isp_app_free(AvrIspApp* app) {
furi_assert(app);
// Disable 5v power
if(furi_hal_power_is_otg_enabled()) {
furi_hal_power_disable_otg();
}
// Submenu
view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewSubmenu);
submenu_free(app->submenu);
// Widget
view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewWidget);
widget_free(app->widget);
// TextInput
view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewTextInput);
text_input_free(app->text_input);
// Popup
view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewPopup);
popup_free(app->popup);
//Dialog
furi_record_close(RECORD_DIALOGS);
// Programmer view
view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewProgrammer);
avr_isp_programmer_view_free(app->avr_isp_programmer_view);
// Reader view
view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewReader);
avr_isp_reader_view_free(app->avr_isp_reader_view);
// Writer view
view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewWriter);
avr_isp_writer_view_free(app->avr_isp_writer_view);
// Chip detect view
view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewChipDetect);
avr_isp_chip_detect_view_free(app->avr_isp_chip_detect_view);
// View dispatcher
view_dispatcher_free(app->view_dispatcher);
scene_manager_free(app->scene_manager);
// Notifications
furi_record_close(RECORD_NOTIFICATION);
app->notifications = NULL;
// Close records
furi_record_close(RECORD_GUI);
// Path strings
furi_string_free(app->file_path);
free(app);
}
int32_t avr_isp_app(void* p) {
UNUSED(p);
AvrIspApp* avr_isp_app = avr_isp_app_alloc();
view_dispatcher_run(avr_isp_app->view_dispatcher);
avr_isp_app_free(avr_isp_app);
return 0;
}

View file

@ -1,31 +0,0 @@
#include "avr_isp_app_i.h"
#include <lib/toolbox/path.h>
#include <flipper_format/flipper_format_i.h>
#define TAG "AvrIsp"
bool avr_isp_load_from_file(AvrIspApp* app) {
furi_assert(app);
FuriString* file_path = furi_string_alloc();
FuriString* file_name = furi_string_alloc();
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(
&browser_options, AVR_ISP_APP_EXTENSION, &I_avr_app_icon_10x10);
browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX;
// Input events and views are managed by file_select
bool res = dialog_file_browser_show(app->dialogs, file_path, app->file_path, &browser_options);
if(res) {
path_extract_dirname(furi_string_get_cstr(file_path), app->file_path);
path_extract_filename(file_path, file_name, true);
strncpy(app->file_name_tmp, furi_string_get_cstr(file_name), AVR_ISP_MAX_LEN_NAME);
}
furi_string_free(file_name);
furi_string_free(file_path);
return res;
}

View file

@ -1,44 +0,0 @@
#pragma once
#include "helpers/avr_isp_types.h"
#include <avr_isp_icons.h>
#include "scenes/avr_isp_scene.h"
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <gui/modules/submenu.h>
#include <gui/modules/widget.h>
#include <notification/notification_messages.h>
#include <gui/modules/text_input.h>
#include <dialogs/dialogs.h>
#include <storage/storage.h>
#include <gui/modules/popup.h>
#include "views/avr_isp_view_programmer.h"
#include "views/avr_isp_view_reader.h"
#include "views/avr_isp_view_writer.h"
#include "views/avr_isp_view_chip_detect.h"
#define AVR_ISP_MAX_LEN_NAME 64
typedef struct {
Gui* gui;
ViewDispatcher* view_dispatcher;
SceneManager* scene_manager;
NotificationApp* notifications;
DialogsApp* dialogs;
Popup* popup;
Submenu* submenu;
Widget* widget;
TextInput* text_input;
FuriString* file_path;
char file_name_tmp[AVR_ISP_MAX_LEN_NAME];
AvrIspProgrammerView* avr_isp_programmer_view;
AvrIspReaderView* avr_isp_reader_view;
AvrIspWriterView* avr_isp_writer_view;
AvrIspChipDetectView* avr_isp_chip_detect_view;
AvrIspError error;
} AvrIspApp;
bool avr_isp_load_from_file(AvrIspApp* app);

View file

@ -1,496 +0,0 @@
#include "avr_isp.h"
#include "../lib/driver/avr_isp_prog_cmd.h"
#include "../lib/driver/avr_isp_spi_sw.h"
#include <furi.h>
#define AVR_ISP_PROG_TX_RX_BUF_SIZE 320
#define TAG "AvrIsp"
struct AvrIsp {
AvrIspSpiSw* spi;
bool pmode;
AvrIspCallback callback;
void* context;
};
AvrIsp* avr_isp_alloc(void) {
AvrIsp* instance = malloc(sizeof(AvrIsp));
return instance;
}
void avr_isp_free(AvrIsp* instance) {
furi_assert(instance);
if(instance->spi) avr_isp_end_pmode(instance);
free(instance);
}
void avr_isp_set_tx_callback(AvrIsp* instance, AvrIspCallback callback, void* context) {
furi_assert(instance);
furi_assert(context);
instance->callback = callback;
instance->context = context;
}
uint8_t avr_isp_spi_transaction(
AvrIsp* instance,
uint8_t cmd,
uint8_t addr_hi,
uint8_t addr_lo,
uint8_t data) {
furi_assert(instance);
avr_isp_spi_sw_txrx(instance->spi, cmd);
avr_isp_spi_sw_txrx(instance->spi, addr_hi);
avr_isp_spi_sw_txrx(instance->spi, addr_lo);
return avr_isp_spi_sw_txrx(instance->spi, data);
}
static bool avr_isp_set_pmode(AvrIsp* instance, uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
furi_assert(instance);
uint8_t res = 0;
avr_isp_spi_sw_txrx(instance->spi, a);
avr_isp_spi_sw_txrx(instance->spi, b);
res = avr_isp_spi_sw_txrx(instance->spi, c);
avr_isp_spi_sw_txrx(instance->spi, d);
return res == 0x53;
}
void avr_isp_end_pmode(AvrIsp* instance) {
furi_assert(instance);
if(instance->pmode) {
avr_isp_spi_sw_res_set(instance->spi, true);
// We're about to take the target out of reset
// so configure SPI pins as input
if(instance->spi) avr_isp_spi_sw_free(instance->spi);
instance->spi = NULL;
}
instance->pmode = false;
}
static bool avr_isp_start_pmode(AvrIsp* instance, AvrIspSpiSwSpeed spi_speed) {
furi_assert(instance);
// Reset target before driving PIN_SCK or PIN_MOSI
// SPI.begin() will configure SS as output,
// so SPI master mode is selected.
// We have defined RESET as pin 10,
// which for many arduino's is not the SS pin.
// So we have to configure RESET as output here,
// (reset_target() first sets the correct level)
if(instance->spi) avr_isp_spi_sw_free(instance->spi);
instance->spi = avr_isp_spi_sw_init(spi_speed);
avr_isp_spi_sw_res_set(instance->spi, false);
// See avr datasheets, chapter "SERIAL_PRG Programming Algorithm":
// Pulse RESET after PIN_SCK is low:
avr_isp_spi_sw_sck_set(instance->spi, false);
// discharge PIN_SCK, value arbitrally chosen
furi_delay_ms(20);
avr_isp_spi_sw_res_set(instance->spi, true);
// Pulse must be minimum 2 target CPU speed cycles
// so 100 usec is ok for CPU speeds above 20KHz
furi_delay_ms(1);
avr_isp_spi_sw_res_set(instance->spi, false);
// Send the enable programming command:
// datasheet: must be > 20 msec
furi_delay_ms(50);
if(avr_isp_set_pmode(instance, AVR_ISP_SET_PMODE)) {
instance->pmode = true;
return true;
}
return false;
}
bool avr_isp_auto_set_spi_speed_start_pmode(AvrIsp* instance) {
furi_assert(instance);
AvrIspSpiSwSpeed spi_speed[] = {
AvrIspSpiSwSpeed1Mhz,
AvrIspSpiSwSpeed400Khz,
AvrIspSpiSwSpeed250Khz,
AvrIspSpiSwSpeed125Khz,
AvrIspSpiSwSpeed60Khz,
AvrIspSpiSwSpeed40Khz,
AvrIspSpiSwSpeed20Khz,
AvrIspSpiSwSpeed10Khz,
AvrIspSpiSwSpeed5Khz,
AvrIspSpiSwSpeed1Khz,
};
for(uint8_t i = 0; i < COUNT_OF(spi_speed); i++) {
if(avr_isp_start_pmode(instance, spi_speed[i])) {
AvrIspSignature sig = avr_isp_read_signature(instance);
AvrIspSignature sig_examination = avr_isp_read_signature(instance); //-V656
uint8_t y = 0;
while(y < 8) {
if(memcmp((uint8_t*)&sig, (uint8_t*)&sig_examination, sizeof(AvrIspSignature)) !=
0)
break;
sig_examination = avr_isp_read_signature(instance);
y++;
}
if(y == 8) {
if(spi_speed[i] > AvrIspSpiSwSpeed1Mhz) {
if(i < (COUNT_OF(spi_speed) - 1)) {
avr_isp_end_pmode(instance);
i++;
return avr_isp_start_pmode(instance, spi_speed[i]);
}
}
return true;
}
}
}
if(instance->spi) {
avr_isp_spi_sw_free(instance->spi);
instance->spi = NULL;
}
return false;
}
static void avr_isp_commit(AvrIsp* instance, uint16_t addr, uint8_t data) {
furi_assert(instance);
avr_isp_spi_transaction(instance, AVR_ISP_COMMIT(addr));
/* polling flash */
if(data == 0xFF) {
furi_delay_ms(5);
} else {
/* polling flash */
uint32_t starttime = furi_get_tick();
while((furi_get_tick() - starttime) < 30) {
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)) != 0xFF) {
break;
};
}
}
}
static uint16_t avr_isp_current_page(AvrIsp* instance, uint32_t addr, uint16_t page_size) {
furi_assert(instance);
uint16_t page = 0;
switch(page_size) {
case 32:
page = addr & 0xFFFFFFF0;
break;
case 64:
page = addr & 0xFFFFFFE0;
break;
case 128:
page = addr & 0xFFFFFFC0;
break;
case 256:
page = addr & 0xFFFFFF80;
break;
default:
page = addr;
break;
}
return page;
}
static bool avr_isp_flash_write_pages(
AvrIsp* instance,
uint16_t addr,
uint16_t page_size,
uint8_t* data,
uint32_t data_size) {
furi_assert(instance);
size_t x = 0;
uint16_t page = avr_isp_current_page(instance, addr, page_size);
while(x < data_size) {
if(page != avr_isp_current_page(instance, addr, page_size)) {
avr_isp_commit(instance, page, data[x - 1]);
page = avr_isp_current_page(instance, addr, page_size);
}
avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FLASH_LO(addr, data[x++]));
avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FLASH_HI(addr, data[x++]));
addr++;
}
avr_isp_commit(instance, page, data[x - 1]);
return true;
}
bool avr_isp_erase_chip(AvrIsp* instance) {
furi_assert(instance);
bool ret = false;
if(!instance->pmode) avr_isp_auto_set_spi_speed_start_pmode(instance);
if(instance->pmode) {
avr_isp_spi_transaction(instance, AVR_ISP_ERASE_CHIP);
furi_delay_ms(100);
avr_isp_end_pmode(instance);
ret = true;
}
return ret;
}
static bool
avr_isp_eeprom_write(AvrIsp* instance, uint16_t addr, uint8_t* data, uint32_t data_size) {
furi_assert(instance);
for(uint16_t i = 0; i < data_size; i++) {
avr_isp_spi_transaction(instance, AVR_ISP_WRITE_EEPROM(addr, data[i]));
furi_delay_ms(10);
addr++;
}
return true;
}
bool avr_isp_write_page(
AvrIsp* instance,
uint32_t mem_type,
uint32_t mem_size,
uint16_t addr,
uint16_t page_size,
uint8_t* data,
uint32_t data_size) {
furi_assert(instance);
bool ret = false;
switch(mem_type) {
case STK_SET_FLASH_TYPE:
if((addr + data_size / 2) <= mem_size) {
ret = avr_isp_flash_write_pages(instance, addr, page_size, data, data_size);
}
break;
case STK_SET_EEPROM_TYPE:
if((addr + data_size) <= mem_size) {
ret = avr_isp_eeprom_write(instance, addr, data, data_size);
}
break;
default:
furi_crash(TAG " Incorrect mem type.");
break;
}
return ret;
}
static bool avr_isp_flash_read_page(
AvrIsp* instance,
uint16_t addr,
uint16_t page_size,
uint8_t* data,
uint32_t data_size) {
furi_assert(instance);
if(page_size > data_size) return false;
for(uint16_t i = 0; i < page_size; i += 2) {
data[i] = avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_LO(addr));
data[i + 1] = avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr));
addr++;
}
return true;
}
static bool avr_isp_eeprom_read_page(
AvrIsp* instance,
uint16_t addr,
uint16_t page_size,
uint8_t* data,
uint32_t data_size) {
furi_assert(instance);
if(page_size > data_size) return false;
for(uint16_t i = 0; i < page_size; i++) {
data[i] = avr_isp_spi_transaction(instance, AVR_ISP_READ_EEPROM(addr));
addr++;
}
return true;
}
bool avr_isp_read_page(
AvrIsp* instance,
uint32_t mem_type,
uint16_t addr,
uint16_t page_size,
uint8_t* data,
uint32_t data_size) {
furi_assert(instance);
bool res = false;
if(mem_type == STK_SET_FLASH_TYPE)
res = avr_isp_flash_read_page(instance, addr, page_size, data, data_size);
if(mem_type == STK_SET_EEPROM_TYPE)
res = avr_isp_eeprom_read_page(instance, addr, page_size, data, data_size);
return res;
}
AvrIspSignature avr_isp_read_signature(AvrIsp* instance) {
furi_assert(instance);
AvrIspSignature signature;
signature.vendor = avr_isp_spi_transaction(instance, AVR_ISP_READ_VENDOR);
signature.part_family = avr_isp_spi_transaction(instance, AVR_ISP_READ_PART_FAMILY);
signature.part_number = avr_isp_spi_transaction(instance, AVR_ISP_READ_PART_NUMBER);
return signature;
}
uint8_t avr_isp_read_lock_byte(AvrIsp* instance) {
furi_assert(instance);
uint8_t data = 0;
uint32_t starttime = furi_get_tick();
while((furi_get_tick() - starttime) < 300) {
data = avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE);
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE) == data) {
break;
};
data = 0x00;
}
return data;
}
bool avr_isp_write_lock_byte(AvrIsp* instance, uint8_t lock) {
furi_assert(instance);
bool ret = false;
if(avr_isp_read_lock_byte(instance) == lock) {
ret = true;
} else {
avr_isp_spi_transaction(instance, AVR_ISP_WRITE_LOCK_BYTE(lock));
/* polling lock byte */
uint32_t starttime = furi_get_tick();
while((furi_get_tick() - starttime) < 30) {
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE) == lock) {
ret = true;
break;
};
}
}
return ret;
}
uint8_t avr_isp_read_fuse_low(AvrIsp* instance) {
furi_assert(instance);
uint8_t data = 0;
uint32_t starttime = furi_get_tick();
while((furi_get_tick() - starttime) < 300) {
data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW);
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW) == data) {
break;
};
data = 0x00;
}
return data;
}
bool avr_isp_write_fuse_low(AvrIsp* instance, uint8_t lfuse) {
furi_assert(instance);
bool ret = false;
if(avr_isp_read_fuse_low(instance) == lfuse) {
ret = true;
} else {
avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FUSE_LOW(lfuse));
/* polling fuse */
uint32_t starttime = furi_get_tick();
while((furi_get_tick() - starttime) < 30) {
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW) == lfuse) {
ret = true;
break;
};
}
}
return ret;
}
uint8_t avr_isp_read_fuse_high(AvrIsp* instance) {
furi_assert(instance);
uint8_t data = 0;
uint32_t starttime = furi_get_tick();
while((furi_get_tick() - starttime) < 300) {
data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH);
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH) == data) {
break;
};
data = 0x00;
}
return data;
}
bool avr_isp_write_fuse_high(AvrIsp* instance, uint8_t hfuse) {
furi_assert(instance);
bool ret = false;
if(avr_isp_read_fuse_high(instance) == hfuse) {
ret = true;
} else {
avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FUSE_HIGH(hfuse));
/* polling fuse */
uint32_t starttime = furi_get_tick();
while((furi_get_tick() - starttime) < 30) {
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH) == hfuse) {
ret = true;
break;
};
}
}
return ret;
}
uint8_t avr_isp_read_fuse_extended(AvrIsp* instance) {
furi_assert(instance);
uint8_t data = 0;
uint32_t starttime = furi_get_tick();
while((furi_get_tick() - starttime) < 300) {
data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED);
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED) == data) {
break;
};
data = 0x00;
}
return data;
}
bool avr_isp_write_fuse_extended(AvrIsp* instance, uint8_t efuse) {
furi_assert(instance);
bool ret = false;
if(avr_isp_read_fuse_extended(instance) == efuse) {
ret = true;
} else {
avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FUSE_EXTENDED(efuse));
/* polling fuse */
uint32_t starttime = furi_get_tick();
while((furi_get_tick() - starttime) < 30) {
if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED) == efuse) {
ret = true;
break;
};
}
}
return ret;
}
void avr_isp_write_extended_addr(AvrIsp* instance, uint8_t extended_addr) {
furi_assert(instance);
avr_isp_spi_transaction(instance, AVR_ISP_EXTENDED_ADDR(extended_addr));
furi_delay_ms(10);
}

View file

@ -1,70 +0,0 @@
#pragma once
#include <furi_hal.h>
typedef struct AvrIsp AvrIsp;
typedef void (*AvrIspCallback)(void* context);
struct AvrIspSignature {
uint8_t vendor;
uint8_t part_family;
uint8_t part_number;
};
typedef struct AvrIspSignature AvrIspSignature;
AvrIsp* avr_isp_alloc(void);
void avr_isp_free(AvrIsp* instance);
void avr_isp_set_tx_callback(AvrIsp* instance, AvrIspCallback callback, void* context);
bool avr_isp_auto_set_spi_speed_start_pmode(AvrIsp* instance);
AvrIspSignature avr_isp_read_signature(AvrIsp* instance);
void avr_isp_end_pmode(AvrIsp* instance);
bool avr_isp_erase_chip(AvrIsp* instance);
uint8_t avr_isp_spi_transaction(
AvrIsp* instance,
uint8_t cmd,
uint8_t addr_hi,
uint8_t addr_lo,
uint8_t data);
bool avr_isp_read_page(
AvrIsp* instance,
uint32_t memtype,
uint16_t addr,
uint16_t page_size,
uint8_t* data,
uint32_t data_size);
bool avr_isp_write_page(
AvrIsp* instance,
uint32_t mem_type,
uint32_t mem_size,
uint16_t addr,
uint16_t page_size,
uint8_t* data,
uint32_t data_size);
uint8_t avr_isp_read_lock_byte(AvrIsp* instance);
bool avr_isp_write_lock_byte(AvrIsp* instance, uint8_t lock);
uint8_t avr_isp_read_fuse_low(AvrIsp* instance);
bool avr_isp_write_fuse_low(AvrIsp* instance, uint8_t lfuse);
uint8_t avr_isp_read_fuse_high(AvrIsp* instance);
bool avr_isp_write_fuse_high(AvrIsp* instance, uint8_t hfuse);
uint8_t avr_isp_read_fuse_extended(AvrIsp* instance);
bool avr_isp_write_fuse_extended(AvrIsp* instance, uint8_t efuse);
void avr_isp_write_extended_addr(AvrIsp* instance, uint8_t extended_addr);

View file

@ -1,23 +0,0 @@
#pragma once
typedef enum {
//SubmenuIndex
SubmenuIndexAvrIspProgrammer = 10,
SubmenuIndexAvrIspReader,
SubmenuIndexAvrIspWriter,
SubmenuIndexAvrIsWiring,
SubmenuIndexAvrIspAbout,
//AvrIspCustomEvent
AvrIspCustomEventSceneChipDetectOk = 100,
AvrIspCustomEventSceneReadingOk,
AvrIspCustomEventSceneWritingOk,
AvrIspCustomEventSceneErrorVerification,
AvrIspCustomEventSceneErrorReading,
AvrIspCustomEventSceneErrorWriting,
AvrIspCustomEventSceneErrorWritingFuse,
AvrIspCustomEventSceneInputName,
AvrIspCustomEventSceneSuccess,
AvrIspCustomEventSceneExit,
AvrIspCustomEventSceneExitStartMenu,
} AvrIspCustomEvent;

View file

@ -1,32 +0,0 @@
#pragma once
#include <furi.h>
#include <furi_hal.h>
#define AVR_ISP_VERSION_APP "0.1"
#define AVR_ISP_DEVELOPED "SkorP"
#define AVR_ISP_GITHUB "https://github.com/flipperdevices/flipperzero-firmware"
#define AVR_ISP_APP_FILE_VERSION 1
#define AVR_ISP_APP_FILE_TYPE "Flipper Dump AVR"
#define AVR_ISP_APP_EXTENSION ".avr"
typedef enum {
//AvrIspViewVariableItemList,
AvrIspViewSubmenu,
AvrIspViewProgrammer,
AvrIspViewReader,
AvrIspViewWriter,
AvrIspViewWidget,
AvrIspViewPopup,
AvrIspViewTextInput,
AvrIspViewChipDetect,
} AvrIspView;
typedef enum {
AvrIspErrorNoError,
AvrIspErrorReading,
AvrIspErrorWriting,
AvrIspErrorVerification,
AvrIspErrorWritingFuse,
} AvrIspError;

View file

@ -1,266 +0,0 @@
#include "avr_isp_worker.h"
#include <furi_hal_pwm.h>
#include "../lib/driver/avr_isp_prog.h"
#include "../lib/driver/avr_isp_prog_cmd.h"
#include "../lib/driver/avr_isp_chip_arr.h"
#include <furi.h>
#define TAG "AvrIspWorker"
typedef enum {
AvrIspWorkerEvtStop = (1 << 0),
AvrIspWorkerEvtRx = (1 << 1),
AvrIspWorkerEvtTxCoplete = (1 << 2),
AvrIspWorkerEvtTx = (1 << 3),
AvrIspWorkerEvtState = (1 << 4),
//AvrIspWorkerEvtCfg = (1 << 5),
} AvrIspWorkerEvt;
struct AvrIspWorker {
FuriThread* thread;
volatile bool worker_running;
uint8_t connect_usb;
AvrIspWorkerCallback callback;
void* context;
};
#define AVR_ISP_WORKER_PROG_ALL_EVENTS (AvrIspWorkerEvtStop)
#define AVR_ISP_WORKER_ALL_EVENTS \
(AvrIspWorkerEvtTx | AvrIspWorkerEvtTxCoplete | AvrIspWorkerEvtRx | AvrIspWorkerEvtStop | \
AvrIspWorkerEvtState)
//########################/* VCP CDC */#############################################
#include "usb_cdc.h"
#include <cli/cli_vcp.h>
#include <cli/cli.h>
#include <furi_hal_usb_cdc.h>
#define AVR_ISP_VCP_CDC_CH 1
#define AVR_ISP_VCP_CDC_PKT_LEN CDC_DATA_SZ
#define AVR_ISP_VCP_UART_RX_BUF_SIZE (AVR_ISP_VCP_CDC_PKT_LEN * 5)
static void vcp_on_cdc_tx_complete(void* context);
static void vcp_on_cdc_rx(void* context);
static void vcp_state_callback(void* context, uint8_t state);
static void vcp_on_cdc_control_line(void* context, uint8_t state);
static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config);
static const CdcCallbacks cdc_cb = {
vcp_on_cdc_tx_complete,
vcp_on_cdc_rx,
vcp_state_callback,
vcp_on_cdc_control_line,
vcp_on_line_config,
};
/* VCP callbacks */
static void vcp_on_cdc_tx_complete(void* context) {
furi_assert(context);
AvrIspWorker* instance = context;
furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtTxCoplete);
}
static void vcp_on_cdc_rx(void* context) {
furi_assert(context);
AvrIspWorker* instance = context;
furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtRx);
}
static void vcp_state_callback(void* context, uint8_t state) {
UNUSED(context);
AvrIspWorker* instance = context;
instance->connect_usb = state;
furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtState);
}
static void vcp_on_cdc_control_line(void* context, uint8_t state) {
UNUSED(context);
UNUSED(state);
}
static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config) {
UNUSED(context);
UNUSED(config);
}
static void avr_isp_worker_vcp_cdc_init(void* context) {
furi_hal_usb_unlock();
Cli* cli = furi_record_open(RECORD_CLI);
//close cli
cli_session_close(cli);
//disable callbacks VCP_CDC=0
furi_hal_cdc_set_callbacks(0, NULL, NULL);
//set 2 cdc
furi_check(furi_hal_usb_set_config(&usb_cdc_dual, NULL) == true);
//open cli VCP_CDC=0
cli_session_open(cli, &cli_vcp);
furi_record_close(RECORD_CLI);
furi_hal_cdc_set_callbacks(AVR_ISP_VCP_CDC_CH, (CdcCallbacks*)&cdc_cb, context);
}
static void avr_isp_worker_vcp_cdc_deinit(void) {
//disable callbacks AVR_ISP_VCP_CDC_CH
furi_hal_cdc_set_callbacks(AVR_ISP_VCP_CDC_CH, NULL, NULL);
Cli* cli = furi_record_open(RECORD_CLI);
//close cli
cli_session_close(cli);
furi_hal_usb_unlock();
//set 1 cdc
furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true);
//open cli VCP_CDC=0
cli_session_open(cli, &cli_vcp);
furi_record_close(RECORD_CLI);
}
//#################################################################################
static int32_t avr_isp_worker_prog_thread(void* context) {
AvrIspProg* prog = context;
FURI_LOG_D(TAG, "AvrIspProgWorker Start");
while(1) {
if(furi_thread_flags_get() & AvrIspWorkerEvtStop) break;
avr_isp_prog_avrisp(prog);
}
FURI_LOG_D(TAG, "AvrIspProgWorker Stop");
return 0;
}
static void avr_isp_worker_prog_tx_data(void* context) {
furi_assert(context);
AvrIspWorker* instance = context;
furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtTx);
}
/** Worker thread
*
* @param context
* @return exit code
*/
static int32_t avr_isp_worker_thread(void* context) {
AvrIspWorker* instance = context;
avr_isp_worker_vcp_cdc_init(instance);
/* start PWM on &gpio_ext_pa4 */
furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50);
AvrIspProg* prog = avr_isp_prog_init();
avr_isp_prog_set_tx_callback(prog, avr_isp_worker_prog_tx_data, instance);
uint8_t buf[AVR_ISP_VCP_UART_RX_BUF_SIZE];
size_t len = 0;
FuriThread* prog_thread =
furi_thread_alloc_ex("AvrIspProgWorker", 1024, avr_isp_worker_prog_thread, prog);
furi_thread_start(prog_thread);
FURI_LOG_D(TAG, "Start");
while(instance->worker_running) {
uint32_t events =
furi_thread_flags_wait(AVR_ISP_WORKER_ALL_EVENTS, FuriFlagWaitAny, FuriWaitForever);
if(events & AvrIspWorkerEvtRx) {
if(avr_isp_prog_spaces_rx(prog) >= AVR_ISP_VCP_CDC_PKT_LEN) {
len = furi_hal_cdc_receive(AVR_ISP_VCP_CDC_CH, buf, AVR_ISP_VCP_CDC_PKT_LEN);
// for(uint8_t i = 0; i < len; i++) {
// FURI_LOG_I(TAG, "--> %X", buf[i]);
// }
avr_isp_prog_rx(prog, buf, len);
} else {
furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtRx);
}
}
if((events & AvrIspWorkerEvtTxCoplete) || (events & AvrIspWorkerEvtTx)) {
len = avr_isp_prog_tx(prog, buf, AVR_ISP_VCP_CDC_PKT_LEN);
// for(uint8_t i = 0; i < len; i++) {
// FURI_LOG_I(TAG, "<-- %X", buf[i]);
// }
if(len > 0) furi_hal_cdc_send(AVR_ISP_VCP_CDC_CH, buf, len);
}
if(events & AvrIspWorkerEvtStop) {
break;
}
if(events & AvrIspWorkerEvtState) {
if(instance->callback)
instance->callback(instance->context, (bool)instance->connect_usb);
}
}
FURI_LOG_D(TAG, "Stop");
furi_thread_flags_set(furi_thread_get_id(prog_thread), AvrIspWorkerEvtStop);
avr_isp_prog_exit(prog);
furi_delay_ms(10);
furi_thread_join(prog_thread);
furi_thread_free(prog_thread);
avr_isp_prog_free(prog);
furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4);
avr_isp_worker_vcp_cdc_deinit();
return 0;
}
AvrIspWorker* avr_isp_worker_alloc(void* context) {
furi_assert(context);
UNUSED(context);
AvrIspWorker* instance = malloc(sizeof(AvrIspWorker));
instance->thread = furi_thread_alloc_ex("AvrIspWorker", 2048, avr_isp_worker_thread, instance);
return instance;
}
void avr_isp_worker_free(AvrIspWorker* instance) {
furi_assert(instance);
furi_check(!instance->worker_running);
furi_thread_free(instance->thread);
free(instance);
}
void avr_isp_worker_set_callback(
AvrIspWorker* instance,
AvrIspWorkerCallback callback,
void* context) {
furi_assert(instance);
instance->callback = callback;
instance->context = context;
}
void avr_isp_worker_start(AvrIspWorker* instance) {
furi_assert(instance);
furi_assert(!instance->worker_running);
instance->worker_running = true;
furi_thread_start(instance->thread);
}
void avr_isp_worker_stop(AvrIspWorker* instance) {
furi_assert(instance);
furi_assert(instance->worker_running);
instance->worker_running = false;
furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtStop);
furi_thread_join(instance->thread);
}
bool avr_isp_worker_is_running(AvrIspWorker* instance) {
furi_assert(instance);
return instance->worker_running;
}

View file

@ -1,49 +0,0 @@
#pragma once
#include <furi_hal.h>
typedef struct AvrIspWorker AvrIspWorker;
typedef void (*AvrIspWorkerCallback)(void* context, bool connect_usb);
/** Allocate AvrIspWorker
*
* @param context AvrIsp* context
* @return AvrIspWorker*
*/
AvrIspWorker* avr_isp_worker_alloc(void* context);
/** Free AvrIspWorker
*
* @param instance AvrIspWorker instance
*/
void avr_isp_worker_free(AvrIspWorker* instance);
/** Callback AvrIspWorker
*
* @param instance AvrIspWorker instance
* @param callback AvrIspWorkerOverrunCallback callback
* @param context
*/
void avr_isp_worker_set_callback(
AvrIspWorker* instance,
AvrIspWorkerCallback callback,
void* context);
/** Start AvrIspWorker
*
* @param instance AvrIspWorker instance
*/
void avr_isp_worker_start(AvrIspWorker* instance);
/** Stop AvrIspWorker
*
* @param instance AvrIspWorker instance
*/
void avr_isp_worker_stop(AvrIspWorker* instance);
/** Check if worker is running
* @param instance AvrIspWorker instance
* @return bool - true if running
*/
bool avr_isp_worker_is_running(AvrIspWorker* instance);

File diff suppressed because it is too large Load diff

View file

@ -1,99 +0,0 @@
#pragma once
#include <furi_hal.h>
typedef struct AvrIspWorkerRW AvrIspWorkerRW;
typedef void (*AvrIspWorkerRWCallback)(
void* context,
const char* name,
bool detect_chip,
uint32_t flash_size);
typedef enum {
AvrIspWorkerRWStatusILDE = 0,
AvrIspWorkerRWStatusEndReading = 1,
AvrIspWorkerRWStatusEndVerification = 2,
AvrIspWorkerRWStatusEndWriting = 3,
AvrIspWorkerRWStatusEndWritingFuse = 4,
AvrIspWorkerRWStatusErrorReading = (-1),
AvrIspWorkerRWStatusErrorVerification = (-2),
AvrIspWorkerRWStatusErrorWriting = (-3),
AvrIspWorkerRWStatusErrorWritingFuse = (-4),
AvrIspWorkerRWStatusReserved = 0x7FFFFFFF, ///< Prevents enum down-size compiler optimization.
} AvrIspWorkerRWStatus;
typedef void (*AvrIspWorkerRWStatusCallback)(void* context, AvrIspWorkerRWStatus status);
AvrIspWorkerRW* avr_isp_worker_rw_alloc(void* context);
void avr_isp_worker_rw_free(AvrIspWorkerRW* instance);
void avr_isp_worker_rw_start(AvrIspWorkerRW* instance);
void avr_isp_worker_rw_stop(AvrIspWorkerRW* instance);
bool avr_isp_worker_rw_is_running(AvrIspWorkerRW* instance);
void avr_isp_worker_rw_set_callback(
AvrIspWorkerRW* instance,
AvrIspWorkerRWCallback callback,
void* context);
void avr_isp_worker_rw_set_callback_status(
AvrIspWorkerRW* instance,
AvrIspWorkerRWStatusCallback callback_status,
void* context_status);
bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance);
float avr_isp_worker_rw_get_progress_flash(AvrIspWorkerRW* instance);
float avr_isp_worker_rw_get_progress_eeprom(AvrIspWorkerRW* instance);
bool avr_isp_worker_rw_read_dump(
AvrIspWorkerRW* instance,
const char* file_path,
const char* file_name);
void avr_isp_worker_rw_read_dump_start(
AvrIspWorkerRW* instance,
const char* file_path,
const char* file_name);
bool avr_isp_worker_rw_verification(
AvrIspWorkerRW* instance,
const char* file_path,
const char* file_name);
void avr_isp_worker_rw_verification_start(
AvrIspWorkerRW* instance,
const char* file_path,
const char* file_name);
bool avr_isp_worker_rw_check_hex(
AvrIspWorkerRW* instance,
const char* file_path,
const char* file_name);
bool avr_isp_worker_rw_write_dump(
AvrIspWorkerRW* instance,
const char* file_path,
const char* file_name);
void avr_isp_worker_rw_write_dump_start(
AvrIspWorkerRW* instance,
const char* file_path,
const char* file_name);
bool avr_isp_worker_rw_write_fuse(
AvrIspWorkerRW* instance,
const char* file_path,
const char* file_name);
void avr_isp_worker_rw_write_fuse_start(
AvrIspWorkerRW* instance,
const char* file_path,
const char* file_name);

View file

@ -1,321 +0,0 @@
#include "flipper_i32hex_file.h"
#include <string.h>
#include <storage/storage.h>
#include <toolbox/stream/stream.h>
#include <toolbox/stream/file_stream.h>
#include <toolbox/hex.h>
//https://en.wikipedia.org/wiki/Intel_HEX
#define TAG "FlipperI32HexFile"
#define COUNT_BYTE_PAYLOAD 32 //how much payload will be used
#define I32HEX_TYPE_DATA 0x00
#define I32HEX_TYPE_END_OF_FILE 0x01
#define I32HEX_TYPE_EXT_LINEAR_ADDR 0x04
#define I32HEX_TYPE_START_LINEAR_ADDR 0x05
struct FlipperI32HexFile {
uint32_t addr;
uint32_t addr_last;
Storage* storage;
Stream* stream;
FuriString* str_data;
FlipperI32HexFileStatus file_open;
};
FlipperI32HexFile* flipper_i32hex_file_open_write(const char* name, uint32_t start_addr) {
furi_assert(name);
FlipperI32HexFile* instance = malloc(sizeof(FlipperI32HexFile));
instance->addr = start_addr;
instance->addr_last = 0;
instance->storage = furi_record_open(RECORD_STORAGE);
instance->stream = file_stream_alloc(instance->storage);
if(file_stream_open(instance->stream, name, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
instance->file_open = FlipperI32HexFileStatusOpenFileWrite;
FURI_LOG_D(TAG, "Open write file %s", name);
} else {
FURI_LOG_E(TAG, "Failed to open file %s", name);
instance->file_open = FlipperI32HexFileStatusErrorNoOpenFile;
}
instance->str_data = furi_string_alloc(instance->storage);
return instance;
}
FlipperI32HexFile* flipper_i32hex_file_open_read(const char* name) {
furi_assert(name);
FlipperI32HexFile* instance = malloc(sizeof(FlipperI32HexFile));
instance->addr = 0;
instance->addr_last = 0;
instance->storage = furi_record_open(RECORD_STORAGE);
instance->stream = file_stream_alloc(instance->storage);
if(file_stream_open(instance->stream, name, FSAM_READ, FSOM_OPEN_EXISTING)) {
instance->file_open = FlipperI32HexFileStatusOpenFileRead;
FURI_LOG_D(TAG, "Open read file %s", name);
} else {
FURI_LOG_E(TAG, "Failed to open file %s", name);
instance->file_open = FlipperI32HexFileStatusErrorNoOpenFile;
}
instance->str_data = furi_string_alloc(instance->storage);
return instance;
}
void flipper_i32hex_file_close(FlipperI32HexFile* instance) {
furi_assert(instance);
furi_string_free(instance->str_data);
file_stream_close(instance->stream);
stream_free(instance->stream);
furi_record_close(RECORD_STORAGE);
}
FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_data(
FlipperI32HexFile* instance,
uint8_t* data,
uint32_t data_size) {
furi_assert(instance);
furi_assert(data);
FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusOK, .data_size = 0};
if(instance->file_open != FlipperI32HexFileStatusOpenFileWrite) {
ret.status = FlipperI32HexFileStatusErrorFileWrite;
}
uint8_t count_byte = 0;
uint32_t ind = 0;
uint8_t crc = 0;
furi_string_reset(instance->str_data);
if((instance->addr_last & 0xFF0000) < (instance->addr & 0xFF0000)) {
crc = 0x02 + 0x04 + ((instance->addr >> 24) & 0xFF) + ((instance->addr >> 16) & 0xFF);
crc = 0x01 + ~crc;
//I32HEX_TYPE_EXT_LINEAR_ADDR
furi_string_cat_printf(
instance->str_data, ":02000004%04lX%02X\r\n", (instance->addr >> 16), crc);
instance->addr_last = instance->addr;
}
while(ind < data_size) {
if((ind + COUNT_BYTE_PAYLOAD) > data_size) {
count_byte = data_size - ind;
} else {
count_byte = COUNT_BYTE_PAYLOAD;
}
//I32HEX_TYPE_DATA
furi_string_cat_printf(
instance->str_data, ":%02X%04lX00", count_byte, (instance->addr & 0xFFFF));
crc = count_byte + ((instance->addr >> 8) & 0xFF) + (instance->addr & 0xFF);
for(uint32_t i = 0; i < count_byte; i++) {
furi_string_cat_printf(instance->str_data, "%02X", *data);
crc += *data++;
}
crc = 0x01 + ~crc;
furi_string_cat_printf(instance->str_data, "%02X\r\n", crc);
ind += count_byte;
instance->addr += count_byte;
}
if(instance->file_open) stream_write_string(instance->stream, instance->str_data);
return ret;
}
FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_end_line(FlipperI32HexFile* instance) {
furi_assert(instance);
FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusOK, .data_size = 0};
if(instance->file_open != FlipperI32HexFileStatusOpenFileWrite) {
ret.status = FlipperI32HexFileStatusErrorFileWrite;
}
furi_string_reset(instance->str_data);
//I32HEX_TYPE_END_OF_FILE
furi_string_cat_printf(instance->str_data, ":00000001FF\r\n");
if(instance->file_open) stream_write_string(instance->stream, instance->str_data);
return ret;
}
void flipper_i32hex_file_bin_to_i32hex_set_addr(FlipperI32HexFile* instance, uint32_t addr) {
furi_assert(instance);
instance->addr = addr;
}
const char* flipper_i32hex_file_get_string(FlipperI32HexFile* instance) {
furi_assert(instance);
return furi_string_get_cstr(instance->str_data);
}
static FlipperI32HexFileRet flipper_i32hex_file_parse_line(
FlipperI32HexFile* instance,
const char* str,
uint8_t* data,
uint32_t data_size) {
furi_assert(instance);
furi_assert(data);
char* str1;
uint32_t data_wrire_ind = 0;
uint32_t data_len = 0;
FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusErrorData, .data_size = 0};
//Search for start of data I32HEX
str1 = strstr(str, ":");
do {
if(str1 == NULL) {
ret.status = FlipperI32HexFileStatusErrorData;
break;
}
str1++;
if(!hex_char_to_uint8(*str1, str1[1], data + data_wrire_ind)) {
ret.status = FlipperI32HexFileStatusErrorData;
break;
}
str1++;
if(++data_wrire_ind > data_size) {
ret.status = FlipperI32HexFileStatusErrorOverflow;
break;
}
data_len = 5 + data[0]; // +5 bytes per header and crc
while(data_len > data_wrire_ind) {
str1++;
if(!hex_char_to_uint8(*str1, str1[1], data + data_wrire_ind)) {
ret.status = FlipperI32HexFileStatusErrorData;
break;
}
str1++;
if(++data_wrire_ind > data_size) {
ret.status = FlipperI32HexFileStatusErrorOverflow;
break;
}
}
ret.status = FlipperI32HexFileStatusOK;
ret.data_size = data_wrire_ind;
} while(0);
return ret;
}
static bool flipper_i32hex_file_check_data(uint8_t* data, uint32_t data_size) {
furi_assert(data);
uint8_t crc = 0;
uint32_t data_read_ind = 0;
if(data[0] > data_size) return false;
while(data_read_ind < data_size - 1) {
crc += data[data_read_ind++];
}
return data[data_size - 1] == ((1 + ~crc) & 0xFF);
}
static FlipperI32HexFileRet flipper_i32hex_file_parse(
FlipperI32HexFile* instance,
const char* str,
uint8_t* data,
uint32_t data_size) {
furi_assert(instance);
furi_assert(data);
FlipperI32HexFileRet ret = flipper_i32hex_file_parse_line(instance, str, data, data_size);
if((ret.status == FlipperI32HexFileStatusOK) && (ret.data_size > 4)) {
switch(data[3]) {
case I32HEX_TYPE_DATA:
if(flipper_i32hex_file_check_data(data, ret.data_size)) {
ret.data_size -= 5;
memcpy(data, data + 4, ret.data_size);
ret.status = FlipperI32HexFileStatusData;
} else {
ret.status = FlipperI32HexFileStatusErrorCrc;
ret.data_size = 0;
}
break;
case I32HEX_TYPE_END_OF_FILE:
if(flipper_i32hex_file_check_data(data, ret.data_size)) {
ret.status = FlipperI32HexFileStatusEofFile;
ret.data_size = 0;
} else {
ret.status = FlipperI32HexFileStatusErrorCrc;
ret.data_size = 0;
}
break;
case I32HEX_TYPE_EXT_LINEAR_ADDR:
if(flipper_i32hex_file_check_data(data, ret.data_size)) {
data[0] = data[4];
data[1] = data[5];
data[3] = 0;
data[4] = 0;
ret.status = FlipperI32HexFileStatusUdateAddr;
ret.data_size = 4;
} else {
ret.status = FlipperI32HexFileStatusErrorCrc;
ret.data_size = 0;
}
break;
case I32HEX_TYPE_START_LINEAR_ADDR:
ret.status = FlipperI32HexFileStatusErrorUnsupportedCommand;
ret.data_size = 0;
break;
default:
ret.status = FlipperI32HexFileStatusErrorUnsupportedCommand;
ret.data_size = 0;
break;
}
} else {
ret.status = FlipperI32HexFileStatusErrorData;
ret.data_size = 0;
}
return ret;
}
bool flipper_i32hex_file_check(FlipperI32HexFile* instance) {
furi_assert(instance);
uint32_t data_size = 280;
uint8_t data[280] = {0};
bool ret = true;
if(instance->file_open != FlipperI32HexFileStatusOpenFileRead) {
FURI_LOG_E(TAG, "File is not open");
ret = false;
} else {
stream_rewind(instance->stream);
while(stream_read_line(instance->stream, instance->str_data)) {
FlipperI32HexFileRet parse_ret = flipper_i32hex_file_parse(
instance, furi_string_get_cstr(instance->str_data), data, data_size);
if(parse_ret.status < 0) {
ret = false;
}
}
stream_rewind(instance->stream);
}
return ret;
}
FlipperI32HexFileRet flipper_i32hex_file_i32hex_to_bin_get_data(
FlipperI32HexFile* instance,
uint8_t* data,
uint32_t data_size) {
furi_assert(instance);
furi_assert(data);
FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusOK, .data_size = 0};
if(instance->file_open != FlipperI32HexFileStatusOpenFileRead) {
ret.status = FlipperI32HexFileStatusErrorFileRead;
} else {
stream_read_line(instance->stream, instance->str_data);
ret = flipper_i32hex_file_parse(
instance, furi_string_get_cstr(instance->str_data), data, data_size);
}
return ret;
}

View file

@ -1,55 +0,0 @@
#pragma once
#include <furi_hal.h>
typedef struct FlipperI32HexFile FlipperI32HexFile;
typedef enum {
FlipperI32HexFileStatusOK = 0,
FlipperI32HexFileStatusData = 2,
FlipperI32HexFileStatusUdateAddr = 3,
FlipperI32HexFileStatusEofFile = 4,
FlipperI32HexFileStatusOpenFileWrite = 5,
FlipperI32HexFileStatusOpenFileRead = 6,
// Errors
FlipperI32HexFileStatusErrorCrc = (-1),
FlipperI32HexFileStatusErrorOverflow = (-2),
FlipperI32HexFileStatusErrorData = (-3),
FlipperI32HexFileStatusErrorUnsupportedCommand = (-4),
FlipperI32HexFileStatusErrorNoOpenFile = (-5),
FlipperI32HexFileStatusErrorFileWrite = (-6),
FlipperI32HexFileStatusErrorFileRead = (-7),
FlipperI32HexFileStatusReserved =
0x7FFFFFFF, ///< Prevents enum down-size compiler optimization.
} FlipperI32HexFileStatus;
typedef struct {
FlipperI32HexFileStatus status;
uint32_t data_size;
} FlipperI32HexFileRet;
FlipperI32HexFile* flipper_i32hex_file_open_write(const char* name, uint32_t start_addr);
FlipperI32HexFile* flipper_i32hex_file_open_read(const char* name);
void flipper_i32hex_file_close(FlipperI32HexFile* instance);
FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_data(
FlipperI32HexFile* instance,
uint8_t* data,
uint32_t data_size);
FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_end_line(FlipperI32HexFile* instance);
const char* flipper_i32hex_file_get_string(FlipperI32HexFile* instance);
void flipper_i32hex_file_bin_to_i32hex_set_addr(FlipperI32HexFile* instance, uint32_t addr);
bool flipper_i32hex_file_check(FlipperI32HexFile* instance);
FlipperI32HexFileRet flipper_i32hex_file_i32hex_to_bin_get_data(
FlipperI32HexFile* instance,
uint8_t* data,
uint32_t data_size);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

View file

@ -1,386 +0,0 @@
#include "avr_isp_chip_arr.h"
#include <furi.h>
//https://github.com/avrdudes/avrdude/blob/master/src/avrintel.c
const AvrIspChipArr avr_isp_chip_arr[] = { // Value of -1 typically means unknown
//{mcu_name, mcuid, family, {sig, na, ture}, flstart, flsize, pgsiz, nb, bootsz, eestart, eesize, ep, rambeg, ramsiz, nf, nl, ni}, // Source
{"ATtiny4", 0, F_AVR8L, {0x1E, 0x8F, 0x0A}, 0, 0x00200, 0x010, 0, 0, 0, 0, 0, 0x0040, 0x0020, 1, 1, 10}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
{"ATtiny5", 1, F_AVR8L, {0x1E, 0x8F, 0x09}, 0, 0x00200, 0x010, 0, 0, 0, 0, 0, 0x0040, 0x0020, 1, 1, 11}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
{"ATtiny9", 2, F_AVR8L, {0x1E, 0x90, 0x08}, 0, 0x00400, 0x010, 0, 0, 0, 0, 0, 0x0040, 0x0020, 1, 1, 10}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
{"ATtiny10", 3, F_AVR8L, {0x1E, 0x90, 0x03}, 0, 0x00400, 0x010, 0, 0, 0, 0, 0, 0x0040, 0x0020, 1, 1, 11}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
{"ATtiny20", 4, F_AVR8L, {0x1E, 0x91, 0x0F}, 0, 0x00800, 0x020, 0, 0, 0, 0, 0, 0x0040, 0x0080, 1, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
{"ATtiny40", 5, F_AVR8L, {0x1E, 0x92, 0x0E}, 0, 0x01000, 0x040, 0, 0, 0, 0, 0, 0x0040, 0x0100, 1, 1, 18}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
{"ATtiny102", 6, F_AVR8L, {0x1E, 0x90, 0x0C}, 0, 0x00400, 0x010, 0, 0, 0, 0, 0, 0x0040, 0x0020, 1, 1, 16}, // atdf, avrdude, boot size (manual)
{"ATtiny104", 7, F_AVR8L, {0x1E, 0x90, 0x0B}, 0, 0x00400, 0x010, 0, 0, 0, 0, 0, 0x0040, 0x0020, 1, 1, 16}, // atdf, avrdude, boot size (manual)
{"ATtiny11", 8, F_AVR8, {0x1E, 0x90, 0x04}, 0, 0x00400, 0x001, 0, 0, 0, 0x0040, 1, 0x0060, 0x0020, 1, 1, 5}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny12", 9, F_AVR8, {0x1E, 0x90, 0x05}, 0, 0x00400, 0x001, 0, 0, 0, 0x0040, 2, 0x0060, 0x0020, 1, 1, 6}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny13", 10, F_AVR8, {0x1E, 0x90, 0x07}, 0, 0x00400, 0x020, 0, 0, 0, 0x0040, 4, 0x0060, 0x0040, 2, 1, 10}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny13A", 11, F_AVR8, {0x1E, 0x90, 0x07}, 0, 0x00400, 0x020, 0, 0, 0, 0x0040, 4, 0x0060, 0x0040, 2, 1, 10}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny15", 12, F_AVR8, {0x1E, 0x90, 0x06}, 0, 0x00400, 0x001, 0, 0, 0, 0x0040, 2, 0x0060, 0x0020, 1, 1, 9}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny22", 13, F_AVR8, {0x1E, 0x91, 0x06}, 0, 0x00800, -1, 0, 0, -1, -1, -1, 0x0060, 0x0080, 1, 1, 3}, // avr-gcc 12.2.0, boot size (manual)
{"ATtiny24", 14, F_AVR8, {0x1E, 0x91, 0x0B}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny24A", 15, F_AVR8, {0x1E, 0x91, 0x0B}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny25", 16, F_AVR8, {0x1E, 0x91, 0x08}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 15}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny26", 17, F_AVR8, {0x1E, 0x91, 0x09}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 2, 1, 12}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny28", 18, F_AVR8, {0x1E, 0x91, 0x07}, 0, 0x00800, 0x002, 0, 0, 0, 0, 0, 0x0060, 0x0020, 1, 1, 6}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny43U", 19, F_AVR8, {0x1E, 0x92, 0x0C}, 0, 0x01000, 0x040, 0, 0, 0, 0x0040, 4, 0x0060, 0x0100, 3, 1, 16}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny44", 20, F_AVR8, {0x1E, 0x92, 0x07}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0060, 0x0100, 3, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny44A", 21, F_AVR8, {0x1E, 0x92, 0x07}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0060, 0x0100, 3, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny45", 22, F_AVR8, {0x1E, 0x92, 0x06}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0060, 0x0100, 3, 1, 15}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny48", 23, F_AVR8, {0x1E, 0x92, 0x09}, 0, 0x01000, 0x040, 0, 0, 0, 0x0040, 4, 0x0100, 0x0100, 3, 1, 20}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny84", 24, F_AVR8, {0x1E, 0x93, 0x0C}, 0, 0x02000, 0x040, 0, 0, 0, 0x0200, 4, 0x0060, 0x0200, 3, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny84A", 25, F_AVR8, {0x1E, 0x93, 0x0C}, 0, 0x02000, 0x040, 0, 0, 0, 0x0200, 4, 0x0060, 0x0200, 3, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny85", 26, F_AVR8, {0x1E, 0x93, 0x0B}, 0, 0x02000, 0x040, 0, 0, 0, 0x0200, 4, 0x0060, 0x0200, 3, 1, 15}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny87", 27, F_AVR8, {0x1E, 0x93, 0x87}, 0, 0x02000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny88", 28, F_AVR8, {0x1E, 0x93, 0x11}, 0, 0x02000, 0x040, 0, 0, 0, 0x0040, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny167", 29, F_AVR8, {0x1E, 0x94, 0x87}, 0, 0x04000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny261", 30, F_AVR8, {0x1E, 0x91, 0x0C}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny261A", 31, F_AVR8, {0x1E, 0x91, 0x0C}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny441", 32, F_AVR8, {0x1E, 0x92, 0x15}, 0, 0x01000, 0x010, 0, 0, 0, 0x0100, 4, 0x0100, 0x0100, 3, 1, 30}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny461", 33, F_AVR8, {0x1E, 0x92, 0x08}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0060, 0x0100, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny461A", 34, F_AVR8, {0x1E, 0x92, 0x08}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0060, 0x0100, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny828", 35, F_AVR8, {0x1E, 0x93, 0x14}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny828R", 36, F_AVR8, {0x1E, 0x93, 0x14}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 26}, // avrdude, from ATtiny828
{"ATtiny841", 37, F_AVR8, {0x1E, 0x93, 0x15}, 0, 0x02000, 0x010, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 30}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny861", 38, F_AVR8, {0x1E, 0x93, 0x0D}, 0, 0x02000, 0x040, 0, 0, 0, 0x0200, 4, 0x0060, 0x0200, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny861A", 39, F_AVR8, {0x1E, 0x93, 0x0D}, 0, 0x02000, 0x040, 0, 0, 0, 0x0200, 4, 0x0060, 0x0200, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny1634", 40, F_AVR8, {0x1E, 0x94, 0x12}, 0, 0x04000, 0x020, 0, 0, 0, 0x0100, 4, 0x0100, 0x0400, 3, 1, 28}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny1634R", 41, F_AVR8, {0x1E, 0x94, 0x12}, 0, 0x04000, 0x020, 0, 0, 0, 0x0100, 4, 0x0100, 0x0400, 3, 1, 28}, // avrdude, from ATtiny1634
{"ATtiny2313", 42, F_AVR8, {0x1E, 0x91, 0x0A}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny2313A", 43, F_AVR8, {0x1E, 0x91, 0x0A}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny4313", 44, F_AVR8, {0x1E, 0x92, 0x0D}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0060, 0x0100, 3, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega8", 45, F_AVR8, {0x1E, 0x93, 0x07}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0060, 0x0400, 2, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega8A", 46, F_AVR8, {0x1E, 0x93, 0x07}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0060, 0x0400, 2, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega8HVA", 47, F_AVR8, {0x1E, 0x93, 0x10}, 0, 0x02000, 0x080, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 1, 1, 21}, // atdf, avr-gcc 12.2.0
{"ATmega8U2", 48, F_AVR8, {0x1E, 0x93, 0x89}, 0, 0x02000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 29}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega16", 49, F_AVR8, {0x1E, 0x94, 0x03}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0060, 0x0400, 2, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega16A", 50, F_AVR8, {0x1E, 0x94, 0x03}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0060, 0x0400, 2, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega16HVA", 51, F_AVR8, {0x1E, 0x94, 0x0C}, 0, 0x04000, 0x080, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 1, 1, 21}, // atdf, avr-gcc 12.2.0
{"ATmega16HVB", 52, F_AVR8, {0x1E, 0x94, 0x0D}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0400, 2, 1, 29}, // atdf, avr-gcc 12.2.0
{"ATmega16HVBrevB", 53, F_AVR8, {0x1E, 0x94, 0x0D}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0400, 2, 1, 29}, // atdf, avr-gcc 12.2.0
{"ATmega16M1", 54, F_AVR8, {0x1E, 0x94, 0x84}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 31}, // atdf, avr-gcc 12.2.0
{"ATmega16HVA2", 55, F_AVR8, {0x1E, 0x94, 0x0E}, 0, 0x04000, 0x080, -1, -1, -1, -1, -1, 0x0100, 0x0400, 2, 1, 22}, // avr-gcc 12.2.0
{"ATmega16U2", 56, F_AVR8, {0x1E, 0x94, 0x89}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 29}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega16U4", 57, F_AVR8, {0x1E, 0x94, 0x88}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0500, 3, 1, 43}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega32", 58, F_AVR8, {0x1E, 0x95, 0x02}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0060, 0x0800, 2, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega32A", 59, F_AVR8, {0x1E, 0x95, 0x02}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0060, 0x0800, 2, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega32HVB", 60, F_AVR8, {0x1E, 0x95, 0x10}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 2, 1, 29}, // atdf, avr-gcc 12.2.0
{"ATmega32HVBrevB", 61, F_AVR8, {0x1E, 0x95, 0x10}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 2, 1, 29}, // atdf, avr-gcc 12.2.0
{"ATmega32C1", 62, F_AVR8, {0x1E, 0x95, 0x86}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 31}, // atdf, avr-gcc 12.2.0
{"ATmega32M1", 63, F_AVR8, {0x1E, 0x95, 0x84}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega32U2", 64, F_AVR8, {0x1E, 0x95, 0x8A}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0400, 3, 1, 29}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega32U4", 65, F_AVR8, {0x1E, 0x95, 0x87}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0a00, 3, 1, 43}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega32U6", 66, F_AVR8, {0x1E, 0x95, 0x88}, 0, 0x08000, 0x080, 4, 0x0200, -1, -1, -1, 0x0100, 0x0a00, 3, 1, 38}, // avr-gcc 12.2.0, boot size (manual)
{"ATmega48", 67, F_AVR8, {0x1E, 0x92, 0x05}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega48A", 68, F_AVR8, {0x1E, 0x92, 0x05}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega48P", 69, F_AVR8, {0x1E, 0x92, 0x0A}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega48PA", 70, F_AVR8, {0x1E, 0x92, 0x0A}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega48PB", 71, F_AVR8, {0x1E, 0x92, 0x10}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 27}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega64", 72, F_AVR8, {0x1E, 0x96, 0x02}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 35}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega64A", 73, F_AVR8, {0x1E, 0x96, 0x02}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 35}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega64HVE", 74, F_AVR8, {0x1E, 0x96, 0x10}, 0, 0x10000, 0x080, 4, 0x0400, -1, -1, -1, 0x0100, 0x1000, 2, 1, 25}, // avr-gcc 12.2.0, boot size (manual)
{"ATmega64C1", 75, F_AVR8, {0x1E, 0x96, 0x86}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 31}, // atdf, avr-gcc 12.2.0
{"ATmega64M1", 76, F_AVR8, {0x1E, 0x96, 0x84}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega64HVE2", 77, F_AVR8, {0x1E, 0x96, 0x10}, 0, 0x10000, 0x080, 4, 0x0400, 0, 0x0400, 4, 0x0100, 0x1000, 2, 1, 25}, // atdf, avr-gcc 12.2.0
{"ATmega64RFR2", 78, F_AVR8, {0x1E, 0xA6, 0x02}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0200, 0x2000, 3, 1, 77}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega88", 79, F_AVR8, {0x1E, 0x93, 0x0A}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega88A", 80, F_AVR8, {0x1E, 0x93, 0x0A}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega88P", 81, F_AVR8, {0x1E, 0x93, 0x0F}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega88PA", 82, F_AVR8, {0x1E, 0x93, 0x0F}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega88PB", 83, F_AVR8, {0x1E, 0x93, 0x16}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 27}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega103", 84, F_AVR8, {0x1E, 0x97, 0x01}, 0, 0x20000, 0x100, 0, 0, 0, 0x1000, 1, 0x0060, 0x0fa0, 1, 1, 24}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"ATmega128", 85, F_AVR8, {0x1E, 0x97, 0x02}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x1000, 3, 1, 35}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega128A", 86, F_AVR8, {0x1E, 0x97, 0x02}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x1000, 3, 1, 35}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega128RFA1", 87, F_AVR8, {0x1E, 0xA7, 0x01}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x4000, 3, 1, 72}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega128RFR2", 88, F_AVR8, {0x1E, 0xA7, 0x02}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x4000, 3, 1, 77}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega161", 89, F_AVR8, {0x1E, 0x94, 0x01}, 0, 0x04000, 0x080, 1, 0x0400, 0, 0x0200, 1, 0x0060, 0x0400, 1, 1, 21}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"ATmega162", 90, F_AVR8, {0x1E, 0x94, 0x04}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 28}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega163", 91, F_AVR8, {0x1E, 0x94, 0x02}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 1, 0x0060, 0x0400, 2, 1, 18}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"ATmega164A", 92, F_AVR8, {0x1E, 0x94, 0x0F}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega164P", 93, F_AVR8, {0x1E, 0x94, 0x0A}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega164PA", 94, F_AVR8, {0x1E, 0x94, 0x0A}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega165", 95, F_AVR8, {0x1E, 0x94, 0x10}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 22}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"ATmega165A", 96, F_AVR8, {0x1E, 0x94, 0x10}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega165P", 97, F_AVR8, {0x1E, 0x94, 0x07}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega165PA", 98, F_AVR8, {0x1E, 0x94, 0x07}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega168", 99, F_AVR8, {0x1E, 0x94, 0x06}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega168A", 100, F_AVR8, {0x1E, 0x94, 0x06}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega168P", 101, F_AVR8, {0x1E, 0x94, 0x0B}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega168PA", 102, F_AVR8, {0x1E, 0x94, 0x0B}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega168PB", 103, F_AVR8, {0x1E, 0x94, 0x15}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 27}, // atdf, avr-gcc 7.3.0, avrdude
{"ATmega169", 104, F_AVR8, {0x1E, 0x94, 0x05}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 23}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"ATmega169A", 105, F_AVR8, {0x1E, 0x94, 0x11}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega169P", 106, F_AVR8, {0x1E, 0x94, 0x05}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega169PA", 107, F_AVR8, {0x1E, 0x94, 0x05}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega256RFR2", 108, F_AVR8, {0x1E, 0xA8, 0x02}, 0, 0x40000, 0x100, 4, 0x0400, 0, 0x2000, 8, 0x0200, 0x8000, 3, 1, 77}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega323", 109, F_AVR8, {0x1E, 0x95, 0x01}, 0, 0x08000, 0x080, 4, 0x0200, -1, -1, -1, 0x0060, 0x0800, 2, 1, 21}, // avr-gcc 12.2.0, boot size (manual)
{"ATmega324A", 110, F_AVR8, {0x1E, 0x95, 0x15}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega324P", 111, F_AVR8, {0x1E, 0x95, 0x08}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega324PA", 112, F_AVR8, {0x1E, 0x95, 0x11}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega324PB", 113, F_AVR8, {0x1E, 0x95, 0x17}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 51}, // atdf, avrdude
{"ATmega325", 114, F_AVR8, {0x1E, 0x95, 0x05}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega325A", 115, F_AVR8, {0x1E, 0x95, 0x05}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega325P", 116, F_AVR8, {0x1E, 0x95, 0x0D}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega325PA", 117, F_AVR8, {0x1E, 0x95, 0x0D}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega328", 118, F_AVR8, {0x1E, 0x95, 0x14}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega328P", 119, F_AVR8, {0x1E, 0x95, 0x0F}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega328PB", 120, F_AVR8, {0x1E, 0x95, 0x16}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 45}, // atdf, avr-gcc 7.3.0, avrdude
{"ATmega329", 121, F_AVR8, {0x1E, 0x95, 0x03}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega329A", 122, F_AVR8, {0x1E, 0x95, 0x03}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega329P", 123, F_AVR8, {0x1E, 0x95, 0x0B}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega329PA", 124, F_AVR8, {0x1E, 0x95, 0x0B}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega406", 125, F_AVR8, {0x1E, 0x95, 0x07}, 0, 0x0a000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0800, 2, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega640", 126, F_AVR8, {0x1E, 0x96, 0x08}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x2000, 3, 1, 57}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega644", 127, F_AVR8, {0x1E, 0x96, 0x09}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 28}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega644A", 128, F_AVR8, {0x1E, 0x96, 0x09}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega644P", 129, F_AVR8, {0x1E, 0x96, 0x0A}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega644PA", 130, F_AVR8, {0x1E, 0x96, 0x0A}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega644RFR2", 131, F_AVR8, {0x1E, 0xA6, 0x03}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0200, 0x2000, 3, 1, 77}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega645", 132, F_AVR8, {0x1E, 0x96, 0x05}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega645A", 133, F_AVR8, {0x1E, 0x96, 0x05}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega645P", 134, F_AVR8, {0x1E, 0x96, 0x0D}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega649", 135, F_AVR8, {0x1E, 0x96, 0x03}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega649A", 136, F_AVR8, {0x1E, 0x96, 0x03}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega649P", 137, F_AVR8, {0x1E, 0x96, 0x0B}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega1280", 138, F_AVR8, {0x1E, 0x97, 0x03}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x2000, 3, 1, 57}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega1281", 139, F_AVR8, {0x1E, 0x97, 0x04}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x2000, 3, 1, 57}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega1284", 140, F_AVR8, {0x1E, 0x97, 0x06}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x4000, 3, 1, 35}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega1284P", 141, F_AVR8, {0x1E, 0x97, 0x05}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x4000, 3, 1, 35}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega1284RFR2", 142, F_AVR8, {0x1E, 0xA7, 0x03}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x4000, 3, 1, 77}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega2560", 143, F_AVR8, {0x1E, 0x98, 0x01}, 0, 0x40000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x2000, 3, 1, 57}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega2561", 144, F_AVR8, {0x1E, 0x98, 0x02}, 0, 0x40000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x2000, 3, 1, 57}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega2564RFR2", 145, F_AVR8, {0x1E, 0xA8, 0x03}, 0, 0x40000, 0x100, 4, 0x0400, 0, 0x2000, 8, 0x0200, 0x8000, 3, 1, 77}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega3250", 146, F_AVR8, {0x1E, 0x95, 0x06}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega3250A", 147, F_AVR8, {0x1E, 0x95, 0x06}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega3250P", 148, F_AVR8, {0x1E, 0x95, 0x0E}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega3250PA", 149, F_AVR8, {0x1E, 0x95, 0x0E}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega3290", 150, F_AVR8, {0x1E, 0x95, 0x04}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega3290A", 151, F_AVR8, {0x1E, 0x95, 0x04}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega3290P", 152, F_AVR8, {0x1E, 0x95, 0x0C}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega3290PA", 153, F_AVR8, {0x1E, 0x95, 0x0C}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega6450", 154, F_AVR8, {0x1E, 0x96, 0x06}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega6450A", 155, F_AVR8, {0x1E, 0x96, 0x06}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega6450P", 156, F_AVR8, {0x1E, 0x96, 0x0E}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega6490", 157, F_AVR8, {0x1E, 0x96, 0x04}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega6490A", 158, F_AVR8, {0x1E, 0x96, 0x04}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega6490P", 159, F_AVR8, {0x1E, 0x96, 0x0C}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega8515", 160, F_AVR8, {0x1E, 0x93, 0x06}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0060, 0x0200, 2, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega8535", 161, F_AVR8, {0x1E, 0x93, 0x08}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0060, 0x0200, 2, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude
{"AT43USB320", 162, F_AVR8, {0xff, -1, -1}, 0, 0x10000, -1, -1, -1, -1, -1, -1, 0x0060, 0x0200, -1, -1, 0}, // avr-gcc 12.2.0
{"AT43USB355", 163, F_AVR8, {0xff, -1, -1}, 0, 0x06000, -1, -1, -1, -1, -1, -1, 0x0060, 0x0400, -1, -1, 0}, // avr-gcc 12.2.0
{"AT76C711", 164, F_AVR8, {0xff, -1, -1}, 0, 0x04000, -1, -1, -1, -1, -1, -1, 0x0060, 0x07a0, -1, -1, 0}, // avr-gcc 12.2.0
{"AT86RF401", 165, F_AVR8, {0x1E, 0x91, 0x81}, 0, 0x00800, -1, -1, -1, -1, -1, -1, 0x0060, 0x0080, 0, 1, 3}, // avr-gcc 12.2.0
{"AT90PWM1", 166, F_AVR8, {0x1E, 0x93, 0x83}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 32}, // atdf, avr-gcc 12.2.0
{"AT90PWM2", 167, F_AVR8, {0x1E, 0x93, 0x81}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 32}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"AT90PWM2B", 168, F_AVR8, {0x1E, 0x93, 0x83}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 32}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90PWM3", 169, F_AVR8, {0x1E, 0x93, 0x81}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 32}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90PWM3B", 170, F_AVR8, {0x1E, 0x93, 0x83}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 32}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90CAN32", 171, F_AVR8, {0x1E, 0x95, 0x81}, 0, 0x08000, 0x100, 4, 0x0400, 0, 0x0400, 8, 0x0100, 0x0800, 3, 1, 37}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90CAN64", 172, F_AVR8, {0x1E, 0x96, 0x81}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 37}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90PWM81", 173, F_AVR8, {0x1E, 0x93, 0x88}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0100, 3, 1, 20}, // atdf, avr-gcc 12.2.0
{"AT90USB82", 174, F_AVR8, {0x1E, 0x93, 0x82}, 0, 0x02000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 29}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90SCR100", 175, F_AVR8, {0x1E, 0x96, 0xC1}, 0, 0x10000, 0x100, 4, 0x0200, -1, -1, -1, 0x0100, 0x1000, 3, 1, 38}, // avr-gcc 12.2.0, boot size (manual)
{"AT90CAN128", 176, F_AVR8, {0x1E, 0x97, 0x81}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x1000, 3, 1, 37}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90PWM161", 177, F_AVR8, {0x1E, 0x94, 0x8B}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 20}, // atdf, avr-gcc 12.2.0
{"AT90USB162", 178, F_AVR8, {0x1E, 0x94, 0x82}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 29}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90PWM216", 179, F_AVR8, {0x1E, 0x94, 0x83}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 32}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90PWM316", 180, F_AVR8, {0x1E, 0x94, 0x83}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 32}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90USB646", 181, F_AVR8, {0x1E, 0x96, 0x82}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 38}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90USB647", 182, F_AVR8, {0x1E, 0x96, 0x82}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 38}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90S1200", 183, F_AVR8, {0x1E, 0x90, 0x01}, 0, 0x00400, 0x001, 0, 0, 0, 0x0040, 1, 0x0060, 0x0020, 1, 1, 4}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"AT90USB1286", 184, F_AVR8, {0x1E, 0x97, 0x82}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x2000, 3, 1, 38}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90USB1287", 185, F_AVR8, {0x1E, 0x97, 0x82}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x2000, 3, 1, 38}, // atdf, avr-gcc 12.2.0, avrdude
{"AT90S2313", 186, F_AVR8, {0x1E, 0x91, 0x01}, 0, 0x00800, 0x001, 0, 0, 0, 0x0080, 1, 0x0060, 0x0080, 1, 1, 11}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"AT90S2323", 187, F_AVR8, {0x1E, 0x91, 0x02}, 0, 0x00800, -1, 0, 0, -1, -1, -1, 0x0060, 0x0080, 1, 1, 3}, // avr-gcc 12.2.0, boot size (manual)
{"AT90S2333", 188, F_AVR8, {0x1E, 0x91, 0x05}, 0, 0x00800, 0x001, 0, 0, 0, 0x0080, 1, 0x0060, 0x0080, -1, -1, 14}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"AT90S2343", 189, F_AVR8, {0x1E, 0x91, 0x03}, 0, 0x00800, 0x001, 0, 0, 0, 0x0080, 1, 0x0060, 0x0080, 1, 1, 3}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"AT90S4414", 190, F_AVR8, {0x1E, 0x92, 0x01}, 0, 0x01000, 0x001, 0, 0, 0, 0x0100, 1, 0x0060, 0x0100, 1, 1, 13}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"AT90S4433", 191, F_AVR8, {0x1E, 0x92, 0x03}, 0, 0x01000, 0x001, 0, 0, 0, 0x0100, 1, 0x0060, 0x0080, 1, 1, 14}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"AT90S4434", 192, F_AVR8, {0x1E, 0x92, 0x02}, 0, 0x01000, 0x001, 0, 0, 0, 0x0100, 1, 0x0060, 0x0100, 1, 1, 17}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"AT90S8515", 193, F_AVR8, {0x1E, 0x93, 0x01}, 0, 0x02000, 0x001, 0, 0, 0, 0x0200, 1, 0x0060, 0x0200, 1, 1, 13}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"AT90C8534", 194, F_AVR8, {0xff, -1, -1}, 0, 0x02000, -1, -1, -1, -1, -1, -1, 0x0060, 0x0100, -1, -1, 0}, // avr-gcc 12.2.0
{"AT90S8535", 195, F_AVR8, {0x1E, 0x93, 0x03}, 0, 0x02000, 0x001, 0, 0, 0, 0x0200, 1, 0x0060, 0x0200, 1, 1, 17}, // avr-gcc 12.2.0, avrdude, boot size (manual)
{"AT94K", 196, F_AVR8, {0xff, -1, -1}, 0, 0x08000, -1, -1, -1, -1, -1, -1, 0x0060, 0x0fa0, -1, -1, 0}, // avr-gcc 12.2.0
{"ATA5272", 197, F_AVR8, {0x1E, 0x93, 0x87}, 0, 0x02000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 37}, // atdf, avr-gcc 12.2.0
{"ATA5505", 198, F_AVR8, {0x1E, 0x94, 0x87}, 0, 0x04000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0
{"ATA5700M322", 199, F_AVR8, {0x1E, 0x95, 0x67}, 0x08000, 0x08000, 0x040, 0, 0, 0, 0x0880, 16, 0x0200, 0x0400, 1, 1, 51}, // atdf
{"ATA5702M322", 200, F_AVR8, {0x1E, 0x95, 0x69}, 0x08000, 0x08000, 0x040, 0, 0, 0, 0x0880, 16, 0x0200, 0x0400, 1, 1, 51}, // atdf, avr-gcc 12.2.0
{"ATA5781", 201, F_AVR8, {0x1E, 0x95, 0x64}, -1, -1, -1, 0, 0, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf
{"ATA5782", 202, F_AVR8, {0x1E, 0x95, 0x65}, 0x08000, 0x05000, 0x040, 1, 0x5000, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf, avr-gcc 12.2.0
{"ATA5783", 203, F_AVR8, {0x1E, 0x95, 0x66}, -1, -1, -1, 0, 0, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf
{"ATA5787", 204, F_AVR8, {0x1E, 0x94, 0x6C}, 0x08000, 0x05200, 0x040, 0, 0, 0, 0x0400, 16, 0x0200, 0x0800, 1, 1, 44}, // atdf
{"ATA5790", 205, F_AVR8, {0x1E, 0x94, 0x61}, 0, 0x04000, 0x080, 1, 0x0800, 0, 0x0800, 16, 0x0100, 0x0200, 1, 1, 30}, // atdf, avr-gcc 12.2.0
{"ATA5790N", 206, F_AVR8, {0x1E, 0x94, 0x62}, 0, 0x04000, 0x080, 1, 0x0800, 0, 0x0800, 16, 0x0100, 0x0200, 1, 1, 31}, // atdf, avr-gcc 12.2.0
{"ATA5791", 207, F_AVR8, {0x1E, 0x94, 0x62}, 0, 0x04000, 0x080, 1, 0x0800, 0, 0x0800, 16, 0x0100, 0x0200, 1, 1, 31}, // atdf, avr-gcc 7.3.0
{"ATA5795", 208, F_AVR8, {0x1E, 0x93, 0x61}, 0, 0x02000, 0x040, 1, 0x0800, 0, 0x0800, 16, 0x0100, 0x0200, 1, 1, 23}, // atdf, avr-gcc 12.2.0
{"ATA5831", 209, F_AVR8, {0x1E, 0x95, 0x61}, 0x08000, 0x05000, 0x040, 1, 0x5000, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf, avr-gcc 12.2.0
{"ATA5832", 210, F_AVR8, {0x1E, 0x95, 0x62}, -1, -1, -1, 0, 0, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf
{"ATA5833", 211, F_AVR8, {0x1E, 0x95, 0x63}, -1, -1, -1, 0, 0, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf
{"ATA5835", 212, F_AVR8, {0x1E, 0x94, 0x6B}, 0x08000, 0x05200, 0x040, 0, 0, 0, 0x0400, 16, 0x0200, 0x0800, 1, 1, 44}, // atdf
{"ATA6285", 213, F_AVR8, {0x1E, 0x93, 0x82}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0140, 4, 0x0100, 0x0200, 2, 1, 27}, // atdf, avr-gcc 12.2.0
{"ATA6286", 214, F_AVR8, {0x1E, 0x93, 0x82}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0140, 4, 0x0100, 0x0200, 2, 1, 27}, // atdf, avr-gcc 12.2.0
{"ATA6289", 215, F_AVR8, {0x1E, 0x93, 0x82}, 0, 0x02000, 0x040, 4, 0x0100, -1, -1, -1, 0x0100, 0x0200, 2, 1, 27}, // avr-gcc 12.2.0, boot size (manual)
{"ATA6612C", 216, F_AVR8, {0x1E, 0x93, 0x0A}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0
{"ATA6613C", 217, F_AVR8, {0x1E, 0x94, 0x06}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0
{"ATA6614Q", 218, F_AVR8, {0x1E, 0x95, 0x0F}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 26}, // atdf, avr-gcc 12.2.0
{"ATA6616C", 219, F_AVR8, {0x1E, 0x93, 0x87}, 0, 0x02000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0
{"ATA6617C", 220, F_AVR8, {0x1E, 0x94, 0x87}, 0, 0x04000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0
{"ATA8210", 221, F_AVR8, {0x1E, 0x95, 0x65}, 0x08000, 0x05000, 0x040, 1, 0x5000, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf, avr-gcc 7.3.0
{"ATA8215", 222, F_AVR8, {0x1E, 0x95, 0x64}, -1, -1, -1, 0, 0, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf
{"ATA8510", 223, F_AVR8, {0x1E, 0x95, 0x61}, 0x08000, 0x05000, 0x040, 1, 0x5000, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf, avr-gcc 7.3.0
{"ATA8515", 224, F_AVR8, {0x1E, 0x95, 0x63}, -1, -1, -1, 0, 0, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf
{"ATA664251", 225, F_AVR8, {0x1E, 0x94, 0x87}, 0, 0x04000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0
{"M3000", 226, F_AVR8, {0xff, -1, -1}, 0, 0x10000, -1, -1, -1, -1, -1, -1, 0x1000, 0x1000, -1, -1, 0}, // avr-gcc 12.2.0
{"LGT8F88P", 227, F_AVR8, {0x1E, 0x93, 0x0F}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // avrdude, from ATmega88
{"LGT8F168P", 228, F_AVR8, {0x1E, 0x94, 0x0B}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // avrdude, from ATmega168P
{"LGT8F328P", 229, F_AVR8, {0x1E, 0x95, 0x0F}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 26}, // avrdude, from ATmega328P
{"ATxmega8E5", 230, F_XMEGA, {0x1E, 0x93, 0x41}, 0, 0x02800, 0x080, 1, 0x0800, 0, 0x0200, 32, 0x2000, 0x0400, 7, 1, 43}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega16A4", 231, F_XMEGA, {0x1E, 0x94, 0x41}, 0, 0x05000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x0800, 6, 1, 94}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega16A4U", 232, F_XMEGA, {0x1E, 0x94, 0x41}, 0, 0x05000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x0800, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega16C4", 233, F_XMEGA, {0x1E, 0x94, 0x43}, 0, 0x05000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x0800, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega16D4", 234, F_XMEGA, {0x1E, 0x94, 0x42}, 0, 0x05000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x0800, 6, 1, 91}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega16E5", 235, F_XMEGA, {0x1E, 0x94, 0x45}, 0, 0x05000, 0x080, 1, 0x1000, 0, 0x0200, 32, 0x2000, 0x0800, 7, 1, 43}, // atdf, avr-gcc 7.3.0, avrdude
{"ATxmega32C3", 236, F_XMEGA, {0x1E, 0x95, 0x49}, 0, 0x09000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0
{"ATxmega32D3", 237, F_XMEGA, {0x1E, 0x95, 0x4A}, 0, 0x09000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 6, 1, 114}, // atdf, avr-gcc 12.2.0
{"ATxmega32A4", 238, F_XMEGA, {0x1E, 0x95, 0x41}, 0, 0x09000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 6, 1, 94}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega32A4U", 239, F_XMEGA, {0x1E, 0x95, 0x41}, 0, 0x09000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega32C4", 240, F_XMEGA, {0x1E, 0x95, 0x44}, 0, 0x09000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega32D4", 241, F_XMEGA, {0x1E, 0x95, 0x42}, 0, 0x09000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 6, 1, 91}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega32E5", 242, F_XMEGA, {0x1E, 0x95, 0x4C}, 0, 0x09000, 0x080, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 7, 1, 43}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega64A1", 243, F_XMEGA, {0x1E, 0x96, 0x4E}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 125}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega64A1U", 244, F_XMEGA, {0x1E, 0x96, 0x4E}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega64B1", 245, F_XMEGA, {0x1E, 0x96, 0x52}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 81}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega64A3", 246, F_XMEGA, {0x1E, 0x96, 0x42}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 122}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega64A3U", 247, F_XMEGA, {0x1E, 0x96, 0x42}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega64B3", 248, F_XMEGA, {0x1E, 0x96, 0x51}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 54}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega64C3", 249, F_XMEGA, {0x1E, 0x96, 0x49}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega64D3", 250, F_XMEGA, {0x1E, 0x96, 0x4A}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 114}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega64A4", 251, F_XMEGA, {0x1E, 0x96, 0x46}, 0, 0x11000, 0x100, -1, -1, 0, 0x0800, 32, -1, -1, -1, -1, 0}, // avrdude
{"ATxmega64A4U", 252, F_XMEGA, {0x1E, 0x96, 0x46}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega64D4", 253, F_XMEGA, {0x1E, 0x96, 0x47}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 91}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega128A1", 254, F_XMEGA, {0x1E, 0x97, 0x4C}, 0, 0x22000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 125}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega128A1revD", 255, F_XMEGA, {0x1E, 0x97, 0x41}, 0, 0x22000, 0x200, -1, -1, 0, 0x0800, 32, -1, -1, -1, -1, 0}, // avrdude
{"ATxmega128A1U", 256, F_XMEGA, {0x1E, 0x97, 0x4C}, 0, 0x22000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega128B1", 257, F_XMEGA, {0x1E, 0x97, 0x4D}, 0, 0x22000, 0x100, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 81}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega128A3", 258, F_XMEGA, {0x1E, 0x97, 0x42}, 0, 0x22000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 122}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega128A3U", 259, F_XMEGA, {0x1E, 0x97, 0x42}, 0, 0x22000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega128B3", 260, F_XMEGA, {0x1E, 0x97, 0x4B}, 0, 0x22000, 0x100, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 54}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega128C3", 261, F_XMEGA, {0x1E, 0x97, 0x52}, 0, 0x22000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega128D3", 262, F_XMEGA, {0x1E, 0x97, 0x48}, 0, 0x22000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 114}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega128A4", 263, F_XMEGA, {0x1E, 0x97, 0x46}, 0, 0x22000, 0x200, -1, -1, 0, 0x0800, 32, -1, -1, -1, -1, 0}, // avrdude
{"ATxmega128A4U", 264, F_XMEGA, {0x1E, 0x97, 0x46}, 0, 0x22000, 0x100, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega128D4", 265, F_XMEGA, {0x1E, 0x97, 0x47}, 0, 0x22000, 0x100, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 91}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega192A1", 266, F_XMEGA, {0x1E, 0x97, 0x4E}, 0, 0x32000, 0x200, -1, -1, 0, 0x0800, 32, -1, -1, -1, -1, 0}, // avrdude
{"ATxmega192A3", 267, F_XMEGA, {0x1E, 0x97, 0x44}, 0, 0x32000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x4000, 6, 1, 122}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega192A3U", 268, F_XMEGA, {0x1E, 0x97, 0x44}, 0, 0x32000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x4000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega192C3", 269, F_XMEGA, {0x1E, 0x97, 0x51}, 0, 0x32000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x4000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega192D3", 270, F_XMEGA, {0x1E, 0x97, 0x49}, 0, 0x32000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x4000, 6, 1, 114}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega256A1", 271, F_XMEGA, {0x1E, 0x98, 0x46}, 0, 0x42000, 0x200, -1, -1, 0, 0x1000, 32, -1, -1, -1, -1, 0}, // avrdude
{"ATxmega256A3", 272, F_XMEGA, {0x1E, 0x98, 0x42}, 0, 0x42000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x4000, 6, 1, 122}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega256A3B", 273, F_XMEGA, {0x1E, 0x98, 0x43}, 0, 0x42000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x4000, 6, 1, 122}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega256A3BU", 274, F_XMEGA, {0x1E, 0x98, 0x43}, 0, 0x42000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x4000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega256A3U", 275, F_XMEGA, {0x1E, 0x98, 0x42}, 0, 0x42000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x4000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega256C3", 276, F_XMEGA, {0x1E, 0x98, 0x46}, 0, 0x42000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x4000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega256D3", 277, F_XMEGA, {0x1E, 0x98, 0x44}, 0, 0x42000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x4000, 6, 1, 114}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega384C3", 278, F_XMEGA, {0x1E, 0x98, 0x45}, 0, 0x62000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x8000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega384D3", 279, F_XMEGA, {0x1E, 0x98, 0x47}, 0, 0x62000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x8000, 6, 1, 114}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny202", 280, F_AVR8X, {0x1E, 0x91, 0x23}, 0, 0x00800, 0x040, 1, 0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny204", 281, F_AVR8X, {0x1E, 0x91, 0x22}, 0, 0x00800, 0x040, 1, 0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny212", 282, F_AVR8X, {0x1E, 0x91, 0x21}, 0, 0x00800, 0x040, 1, 0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny214", 283, F_AVR8X, {0x1E, 0x91, 0x20}, 0, 0x00800, 0x040, 1, 0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny402", 284, F_AVR8X, {0x1E, 0x92, 0x27}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny404", 285, F_AVR8X, {0x1E, 0x92, 0x26}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny406", 286, F_AVR8X, {0x1E, 0x92, 0x25}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny412", 287, F_AVR8X, {0x1E, 0x92, 0x23}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny414", 288, F_AVR8X, {0x1E, 0x92, 0x22}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny416", 289, F_AVR8X, {0x1E, 0x92, 0x21}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny416auto", 290, F_AVR8X, {0x1E, 0x92, 0x28}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf
{"ATtiny417", 291, F_AVR8X, {0x1E, 0x92, 0x20}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny424", 292, F_AVR8X, {0x1E, 0x92, 0x2C}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 30}, // atdf, avrdude
{"ATtiny426", 293, F_AVR8X, {0x1E, 0x92, 0x2B}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 30}, // atdf, avrdude
{"ATtiny427", 294, F_AVR8X, {0x1E, 0x92, 0x2A}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 30}, // atdf, avrdude
{"ATtiny804", 295, F_AVR8X, {0x1E, 0x93, 0x25}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny806", 296, F_AVR8X, {0x1E, 0x93, 0x24}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny807", 297, F_AVR8X, {0x1E, 0x93, 0x23}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny814", 298, F_AVR8X, {0x1E, 0x93, 0x22}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny816", 299, F_AVR8X, {0x1E, 0x93, 0x21}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny817", 300, F_AVR8X, {0x1E, 0x93, 0x20}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny824", 301, F_AVR8X, {0x1E, 0x93, 0x29}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3c00, 0x0400, 10, 1, 30}, // atdf, avrdude
{"ATtiny826", 302, F_AVR8X, {0x1E, 0x93, 0x28}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3c00, 0x0400, 10, 1, 30}, // atdf, avrdude
{"ATtiny827", 303, F_AVR8X, {0x1E, 0x93, 0x27}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3c00, 0x0400, 10, 1, 30}, // atdf, avrdude
{"ATtiny1604", 304, F_AVR8X, {0x1E, 0x94, 0x25}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny1606", 305, F_AVR8X, {0x1E, 0x94, 0x24}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny1607", 306, F_AVR8X, {0x1E, 0x94, 0x23}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny1614", 307, F_AVR8X, {0x1E, 0x94, 0x22}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny1616", 308, F_AVR8X, {0x1E, 0x94, 0x21}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny1617", 309, F_AVR8X, {0x1E, 0x94, 0x20}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny1624", 310, F_AVR8X, {0x1E, 0x94, 0x2A}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 30}, // atdf, avrdude
{"ATtiny1626", 311, F_AVR8X, {0x1E, 0x94, 0x29}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 30}, // atdf, avrdude
{"ATtiny1627", 312, F_AVR8X, {0x1E, 0x94, 0x28}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 30}, // atdf, avrdude
{"ATtiny3214", 313, F_AVR8X, {0x1E, 0x95, 0x20}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3800, 0x0800, 10, 1, 31}, // avr-gcc 12.2.0
{"ATtiny3216", 314, F_AVR8X, {0x1E, 0x95, 0x21}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3800, 0x0800, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny3217", 315, F_AVR8X, {0x1E, 0x95, 0x22}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3800, 0x0800, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude
{"ATtiny3224", 316, F_AVR8X, {0x1E, 0x95, 0x28}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3400, 0x0c00, 10, 1, 30}, // atdf, avrdude
{"ATtiny3226", 317, F_AVR8X, {0x1E, 0x95, 0x27}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3400, 0x0c00, 10, 1, 30}, // atdf, avrdude
{"ATtiny3227", 318, F_AVR8X, {0x1E, 0x95, 0x26}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3400, 0x0c00, 10, 1, 30}, // atdf, avrdude
{"ATmega808", 319, F_AVR8X, {0x1E, 0x93, 0x26}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10, 1, 36}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega809", 320, F_AVR8X, {0x1E, 0x93, 0x2A}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10, 1, 40}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega1608", 321, F_AVR8X, {0x1E, 0x94, 0x27}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 36}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega1609", 322, F_AVR8X, {0x1E, 0x94, 0x26}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 40}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega3208", 323, F_AVR8X, {0x1E, 0x95, 0x30}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3000, 0x1000, 10, 1, 36}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega3209", 324, F_AVR8X, {0x1E, 0x95, 0x31}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3000, 0x1000, 10, 1, 40}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega4808", 325, F_AVR8X, {0x1E, 0x96, 0x50}, 0, 0x0c000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x2800, 0x1800, 10, 1, 36}, // atdf, avr-gcc 12.2.0, avrdude
{"ATmega4809", 326, F_AVR8X, {0x1E, 0x96, 0x51}, 0, 0x0c000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x2800, 0x1800, 10, 1, 40}, // atdf, avr-gcc 12.2.0, avrdude
{"AVR8EA28", 327, F_AVR8X, {0x1E, 0x93, 0x2C}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude
{"AVR8EA32", 328, F_AVR8X, {0x1E, 0x93, 0x2B}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude
{"AVR16DD14", 329, F_AVR8X, {0x1E, 0x94, 0x34}, 0, 0x04000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7800, 0x0800, 16, 4, 36}, // atdf, avrdude
{"AVR16DD20", 330, F_AVR8X, {0x1E, 0x94, 0x33}, 0, 0x04000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7800, 0x0800, 16, 4, 36}, // atdf, avrdude
{"AVR16DD28", 331, F_AVR8X, {0x1E, 0x94, 0x32}, 0, 0x04000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7800, 0x0800, 16, 4, 36}, // atdf, avrdude
{"AVR16EA28", 332, F_AVR8X, {0x1E, 0x94, 0x37}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude
{"AVR16DD32", 333, F_AVR8X, {0x1E, 0x94, 0x31}, 0, 0x04000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7800, 0x0800, 16, 4, 36}, // atdf, avrdude
{"AVR16EA32", 334, F_AVR8X, {0x1E, 0x94, 0x36}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude
{"AVR16EA48", 335, F_AVR8X, {0x1E, 0x94, 0x35}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude
{"AVR32DD14", 336, F_AVR8X, {0x1E, 0x95, 0x3B}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7000, 0x1000, 16, 4, 36}, // atdf, avrdude
{"AVR32DD20", 337, F_AVR8X, {0x1E, 0x95, 0x3A}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7000, 0x1000, 16, 4, 36}, // atdf, avrdude
{"AVR32DA28", 338, F_AVR8X, {0x1E, 0x95, 0x34}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x7000, 0x1000, 16, 4, 41}, // atdf, avrdude
{"AVR32DB28", 339, F_AVR8X, {0x1E, 0x95, 0x37}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x7000, 0x1000, 16, 4, 42}, // atdf, avrdude
{"AVR32DD28", 340, F_AVR8X, {0x1E, 0x95, 0x39}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7000, 0x1000, 16, 4, 36}, // atdf, avrdude
{"AVR32EA28", 341, F_AVR8X, {0x1E, 0x95, 0x3E}, 0, 0x08000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude
{"AVR32DA32", 342, F_AVR8X, {0x1E, 0x95, 0x33}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x7000, 0x1000, 16, 4, 44}, // atdf, avrdude
{"AVR32DB32", 343, F_AVR8X, {0x1E, 0x95, 0x36}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x7000, 0x1000, 16, 4, 44}, // atdf, avrdude
{"AVR32DD32", 344, F_AVR8X, {0x1E, 0x95, 0x38}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7000, 0x1000, 16, 4, 36}, // atdf, avrdude
{"AVR32EA32", 345, F_AVR8X, {0x1E, 0x95, 0x3D}, 0, 0x08000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude
{"AVR32DA48", 346, F_AVR8X, {0x1E, 0x95, 0x32}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x7000, 0x1000, 16, 4, 58}, // atdf, avrdude
{"AVR32DB48", 347, F_AVR8X, {0x1E, 0x95, 0x35}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x7000, 0x1000, 16, 4, 61}, // atdf, avrdude
{"AVR32EA48", 348, F_AVR8X, {0x1E, 0x95, 0x3C}, 0, 0x08000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude
{"AVR64DD14", 349, F_AVR8X, {0x1E, 0x96, 0x1D}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x6000, 0x2000, 16, 4, 36}, // atdf, avrdude
{"AVR64DD20", 350, F_AVR8X, {0x1E, 0x96, 0x1C}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x6000, 0x2000, 16, 4, 36}, // atdf, avrdude
{"AVR64DA28", 351, F_AVR8X, {0x1E, 0x96, 0x15}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 41}, // atdf, avrdude
{"AVR64DB28", 352, F_AVR8X, {0x1E, 0x96, 0x19}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 42}, // atdf, avrdude
{"AVR64DD28", 353, F_AVR8X, {0x1E, 0x96, 0x1B}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x6000, 0x2000, 16, 4, 36}, // atdf, avrdude
{"AVR64EA28", 354, F_AVR8X, {0x1E, 0x96, 0x20}, 0, 0x10000, 0x080, 1, 0, 0x01400, 0x0200, 8, 0x6800, 0x1800, 16, 4, 37}, // atdf, avrdude
{"AVR64DA32", 355, F_AVR8X, {0x1E, 0x96, 0x14}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 44}, // atdf, avrdude
{"AVR64DB32", 356, F_AVR8X, {0x1E, 0x96, 0x18}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 44}, // atdf, avrdude
{"AVR64DD32", 357, F_AVR8X, {0x1E, 0x96, 0x1A}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x6000, 0x2000, 16, 4, 36}, // atdf, avrdude
{"AVR64EA32", 358, F_AVR8X, {0x1E, 0x96, 0x1F}, 0, 0x10000, 0x080, 1, 0, 0x01400, 0x0200, 8, 0x6800, 0x1800, 16, 4, 37}, // atdf, avrdude
{"AVR64DA48", 359, F_AVR8X, {0x1E, 0x96, 0x13}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 58}, // atdf, avrdude
{"AVR64DB48", 360, F_AVR8X, {0x1E, 0x96, 0x17}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 61}, // atdf, avrdude
{"AVR64EA48", 361, F_AVR8X, {0x1E, 0x96, 0x1E}, 0, 0x10000, 0x080, 1, 0, 0x01400, 0x0200, 8, 0x6800, 0x1800, 16, 4, 45}, // atdf, avrdude
{"AVR64DA64", 362, F_AVR8X, {0x1E, 0x96, 0x12}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 64}, // atdf, avrdude
{"AVR64DB64", 363, F_AVR8X, {0x1E, 0x96, 0x16}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 65}, // atdf, avrdude
{"AVR128DA28", 364, F_AVR8X, {0x1E, 0x97, 0x0A}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 41}, // atdf, avrdude
{"AVR128DB28", 365, F_AVR8X, {0x1E, 0x97, 0x0E}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 42}, // atdf, avrdude
{"AVR128DA32", 366, F_AVR8X, {0x1E, 0x97, 0x09}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 44}, // atdf, avrdude
{"AVR128DB32", 367, F_AVR8X, {0x1E, 0x97, 0x0D}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 44}, // atdf, avrdude
{"AVR128DA48", 368, F_AVR8X, {0x1E, 0x97, 0x08}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 58}, // atdf, avrdude
{"AVR128DB48", 369, F_AVR8X, {0x1E, 0x97, 0x0C}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 61}, // atdf, avrdude
{"AVR128DA64", 370, F_AVR8X, {0x1E, 0x97, 0x07}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 64}, // atdf, avrdude
{"AVR128DB64", 371, F_AVR8X, {0x1E, 0x97, 0x0B}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 65}, // atdf, avrdude
};
const size_t avr_isp_chip_arr_size = COUNT_OF(avr_isp_chip_arr);

View file

@ -1,33 +0,0 @@
#pragma once
#include <furi_hal.h>
#define F_AVR8L 1 // TPI programming, ATtiny(4|5|9|10|20|40|102|104)
#define F_AVR8 2 // ISP programming with SPI, "classic" AVRs
#define F_XMEGA 4 // PDI programming, ATxmega family
#define F_AVR8X 8 // UPDI programming, newer 8-bit MCUs
struct AvrIspChipArr { // Value of -1 typically means unknown
const char* name; // Name of part
uint16_t mcuid; // ID of MCU in 0..2039
uint8_t avrarch; // F_AVR8L, F_AVR8, F_XMEGA or F_AVR8X
uint8_t sigs[3]; // Signature bytes
int32_t flashoffset; // Flash offset
int32_t flashsize; // Flash size
int16_t pagesize; // Flash page size
int8_t nboots; // Number of supported boot sectors
int16_t bootsize; // Size of (smallest) boot sector
int32_t eepromoffset; // EEPROM offset
int32_t eepromsize; // EEPROM size
int32_t eeprompagesize; // EEPROM page size
int32_t sramstart; // SRAM offset
int32_t sramsize; // SRAM size
int8_t nfuses; // Number of fuse bytes
int8_t nlocks; // Number of lock bytes
uint8_t ninterrupts; // Number of vectors in interrupt vector table
};
typedef struct AvrIspChipArr AvrIspChipArr;
extern const AvrIspChipArr avr_isp_chip_arr[];
extern const size_t avr_isp_chip_arr_size;

View file

@ -1,639 +0,0 @@
#include "avr_isp_prog.h"
#include "avr_isp_prog_cmd.h"
#include <furi.h>
#define AVR_ISP_PROG_TX_RX_BUF_SIZE 320
#define TAG "AvrIspProg"
struct AvrIspProgSignature {
uint8_t vendor;
uint8_t part_family;
uint8_t part_number;
};
typedef struct AvrIspProgSignature AvrIspProgSignature;
struct AvrIspProgCfgDevice {
uint8_t devicecode;
uint8_t revision;
uint8_t progtype;
uint8_t parmode;
uint8_t polling;
uint8_t selftimed;
uint8_t lockbytes;
uint8_t fusebytes;
uint8_t flashpoll;
uint16_t eeprompoll;
uint16_t pagesize;
uint16_t eepromsize;
uint32_t flashsize;
};
typedef struct AvrIspProgCfgDevice AvrIspProgCfgDevice;
struct AvrIspProg {
AvrIspSpiSw* spi;
AvrIspProgCfgDevice* cfg;
FuriStreamBuffer* stream_rx;
FuriStreamBuffer* stream_tx;
uint16_t error;
uint16_t addr;
bool pmode;
bool exit;
bool rst_active_high;
uint8_t buff[AVR_ISP_PROG_TX_RX_BUF_SIZE];
AvrIspProgCallback callback;
void* context;
};
static void avr_isp_prog_end_pmode(AvrIspProg* instance);
AvrIspProg* avr_isp_prog_init(void) {
AvrIspProg* instance = malloc(sizeof(AvrIspProg));
instance->cfg = malloc(sizeof(AvrIspProgCfgDevice));
instance->stream_rx =
furi_stream_buffer_alloc(sizeof(int8_t) * AVR_ISP_PROG_TX_RX_BUF_SIZE, sizeof(int8_t));
instance->stream_tx =
furi_stream_buffer_alloc(sizeof(int8_t) * AVR_ISP_PROG_TX_RX_BUF_SIZE, sizeof(int8_t));
instance->rst_active_high = false;
instance->exit = false;
return instance;
}
void avr_isp_prog_free(AvrIspProg* instance) {
furi_assert(instance);
if(instance->spi) avr_isp_prog_end_pmode(instance);
furi_stream_buffer_free(instance->stream_tx);
furi_stream_buffer_free(instance->stream_rx);
free(instance->cfg);
free(instance);
}
size_t avr_isp_prog_spaces_rx(AvrIspProg* instance) {
return furi_stream_buffer_spaces_available(instance->stream_rx);
}
bool avr_isp_prog_rx(AvrIspProg* instance, uint8_t* data, size_t len) {
furi_assert(instance);
furi_assert(data);
furi_assert(len != 0);
size_t ret = furi_stream_buffer_send(instance->stream_rx, data, sizeof(uint8_t) * len, 0);
return ret == sizeof(uint8_t) * len;
}
size_t avr_isp_prog_tx(AvrIspProg* instance, uint8_t* data, size_t max_len) {
furi_assert(instance);
return furi_stream_buffer_receive(instance->stream_tx, data, sizeof(int8_t) * max_len, 0);
}
void avr_isp_prog_exit(AvrIspProg* instance) {
furi_assert(instance);
instance->exit = true;
}
void avr_isp_prog_set_tx_callback(AvrIspProg* instance, AvrIspProgCallback callback, void* context) {
furi_assert(instance);
furi_assert(context);
instance->callback = callback;
instance->context = context;
}
static void avr_isp_prog_tx_ch(AvrIspProg* instance, uint8_t data) {
furi_assert(instance);
furi_stream_buffer_send(instance->stream_tx, &data, sizeof(uint8_t), FuriWaitForever);
}
static uint8_t avr_isp_prog_getch(AvrIspProg* instance) {
furi_assert(instance);
uint8_t data[1] = {0};
while(furi_stream_buffer_receive(instance->stream_rx, &data, sizeof(int8_t), 30) == 0) {
if(instance->exit) break;
};
return data[0];
}
static void avr_isp_prog_fill(AvrIspProg* instance, size_t len) {
furi_assert(instance);
for(size_t x = 0; x < len; x++) {
instance->buff[x] = avr_isp_prog_getch(instance);
}
}
static void avr_isp_prog_reset_target(AvrIspProg* instance, bool reset) {
furi_assert(instance);
avr_isp_spi_sw_res_set(instance->spi, (reset == instance->rst_active_high) ? true : false);
}
static uint8_t avr_isp_prog_spi_transaction(
AvrIspProg* instance,
uint8_t cmd,
uint8_t addr_hi,
uint8_t addr_lo,
uint8_t data) {
furi_assert(instance);
avr_isp_spi_sw_txrx(instance->spi, cmd);
avr_isp_spi_sw_txrx(instance->spi, addr_hi);
avr_isp_spi_sw_txrx(instance->spi, addr_lo);
return avr_isp_spi_sw_txrx(instance->spi, data);
}
static void avr_isp_prog_empty_reply(AvrIspProg* instance) {
furi_assert(instance);
if(avr_isp_prog_getch(instance) == CRC_EOP) {
avr_isp_prog_tx_ch(instance, STK_INSYNC);
avr_isp_prog_tx_ch(instance, STK_OK);
} else {
instance->error++;
avr_isp_prog_tx_ch(instance, STK_NOSYNC);
}
}
static void avr_isp_prog_breply(AvrIspProg* instance, uint8_t data) {
furi_assert(instance);
if(avr_isp_prog_getch(instance) == CRC_EOP) {
avr_isp_prog_tx_ch(instance, STK_INSYNC);
avr_isp_prog_tx_ch(instance, data);
avr_isp_prog_tx_ch(instance, STK_OK);
} else {
instance->error++;
avr_isp_prog_tx_ch(instance, STK_NOSYNC);
}
}
static void avr_isp_prog_get_version(AvrIspProg* instance, uint8_t data) {
furi_assert(instance);
switch(data) {
case STK_HW_VER:
avr_isp_prog_breply(instance, AVR_ISP_HWVER);
break;
case STK_SW_MAJOR:
avr_isp_prog_breply(instance, AVR_ISP_SWMAJ);
break;
case STK_SW_MINOR:
avr_isp_prog_breply(instance, AVR_ISP_SWMIN);
break;
case AVP_ISP_CONNECT_TYPE:
avr_isp_prog_breply(instance, AVP_ISP_SERIAL_CONNECT_TYPE);
break;
default:
avr_isp_prog_breply(instance, AVR_ISP_RESP_0);
}
}
static void avr_isp_prog_set_cfg(AvrIspProg* instance) {
furi_assert(instance);
// call this after reading cfg packet into buff[]
instance->cfg->devicecode = instance->buff[0];
instance->cfg->revision = instance->buff[1];
instance->cfg->progtype = instance->buff[2];
instance->cfg->parmode = instance->buff[3];
instance->cfg->polling = instance->buff[4];
instance->cfg->selftimed = instance->buff[5];
instance->cfg->lockbytes = instance->buff[6];
instance->cfg->fusebytes = instance->buff[7];
instance->cfg->flashpoll = instance->buff[8];
// ignore (instance->buff[9] == instance->buff[8]) //FLASH polling value. Same as <20>flashpoll<6C>
instance->cfg->eeprompoll = instance->buff[10] << 8 | instance->buff[11];
instance->cfg->pagesize = instance->buff[12] << 8 | instance->buff[13];
instance->cfg->eepromsize = instance->buff[14] << 8 | instance->buff[15];
instance->cfg->flashsize = instance->buff[16] << 24 | instance->buff[17] << 16 |
instance->buff[18] << 8 | instance->buff[19];
// avr devices have active low reset, at89sx are active high
instance->rst_active_high = (instance->cfg->devicecode >= 0xe0);
}
static bool
avr_isp_prog_set_pmode(AvrIspProg* instance, uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
furi_assert(instance);
uint8_t res = 0;
avr_isp_spi_sw_txrx(instance->spi, a);
avr_isp_spi_sw_txrx(instance->spi, b);
res = avr_isp_spi_sw_txrx(instance->spi, c);
avr_isp_spi_sw_txrx(instance->spi, d);
return res == 0x53;
}
static void avr_isp_prog_end_pmode(AvrIspProg* instance) {
furi_assert(instance);
if(instance->pmode) {
avr_isp_prog_reset_target(instance, false);
// We're about to take the target out of reset
// so configure SPI pins as input
if(instance->spi) avr_isp_spi_sw_free(instance->spi);
instance->spi = NULL;
}
instance->pmode = false;
}
static bool avr_isp_prog_start_pmode(AvrIspProg* instance, AvrIspSpiSwSpeed spi_speed) {
furi_assert(instance);
// Reset target before driving PIN_SCK or PIN_MOSI
// SPI.begin() will configure SS as output,
// so SPI master mode is selected.
// We have defined RESET as pin 10,
// which for many arduino's is not the SS pin.
// So we have to configure RESET as output here,
// (reset_target() first sets the correct level)
if(instance->spi) avr_isp_spi_sw_free(instance->spi);
instance->spi = avr_isp_spi_sw_init(spi_speed);
avr_isp_prog_reset_target(instance, true);
// See avr datasheets, chapter "SERIAL_PRG Programming Algorithm":
// Pulse RESET after PIN_SCK is low:
avr_isp_spi_sw_sck_set(instance->spi, false);
// discharge PIN_SCK, value arbitrally chosen
furi_delay_ms(20);
avr_isp_prog_reset_target(instance, false);
// Pulse must be minimum 2 target CPU speed cycles
// so 100 usec is ok for CPU speeds above 20KHz
furi_delay_ms(1);
avr_isp_prog_reset_target(instance, true);
// Send the enable programming command:
// datasheet: must be > 20 msec
furi_delay_ms(50);
if(avr_isp_prog_set_pmode(instance, AVR_ISP_SET_PMODE)) {
instance->pmode = true;
return true;
}
return false;
}
static AvrIspProgSignature avr_isp_prog_check_signature(AvrIspProg* instance) {
furi_assert(instance);
AvrIspProgSignature signature;
signature.vendor = avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_VENDOR);
signature.part_family = avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_PART_FAMILY);
signature.part_number = avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_PART_NUMBER);
return signature;
}
static bool avr_isp_prog_auto_set_spi_speed_start_pmode(AvrIspProg* instance) {
AvrIspSpiSwSpeed spi_speed[] = {
AvrIspSpiSwSpeed1Mhz,
AvrIspSpiSwSpeed400Khz,
AvrIspSpiSwSpeed250Khz,
AvrIspSpiSwSpeed125Khz,
AvrIspSpiSwSpeed60Khz,
AvrIspSpiSwSpeed40Khz,
AvrIspSpiSwSpeed20Khz,
AvrIspSpiSwSpeed10Khz,
AvrIspSpiSwSpeed5Khz,
AvrIspSpiSwSpeed1Khz,
};
for(uint8_t i = 0; i < COUNT_OF(spi_speed); i++) {
if(avr_isp_prog_start_pmode(instance, spi_speed[i])) {
AvrIspProgSignature sig = avr_isp_prog_check_signature(instance);
AvrIspProgSignature sig_examination = avr_isp_prog_check_signature(instance); //-V656
uint8_t y = 0;
while(y < 8) {
if(memcmp(
(uint8_t*)&sig, (uint8_t*)&sig_examination, sizeof(AvrIspProgSignature)) !=
0)
break;
sig_examination = avr_isp_prog_check_signature(instance);
y++;
}
if(y == 8) {
if(spi_speed[i] > AvrIspSpiSwSpeed1Mhz) {
if(i < (COUNT_OF(spi_speed) - 1)) {
avr_isp_prog_end_pmode(instance);
i++;
return avr_isp_prog_start_pmode(instance, spi_speed[i]);
}
}
return true;
}
}
}
if(instance->spi) {
avr_isp_spi_sw_free(instance->spi);
instance->spi = NULL;
}
return false;
}
static void avr_isp_prog_universal(AvrIspProg* instance) {
furi_assert(instance);
uint8_t data;
avr_isp_prog_fill(instance, 4);
data = avr_isp_prog_spi_transaction(
instance, instance->buff[0], instance->buff[1], instance->buff[2], instance->buff[3]);
avr_isp_prog_breply(instance, data);
}
static void avr_isp_prog_commit(AvrIspProg* instance, uint16_t addr, uint8_t data) {
furi_assert(instance);
avr_isp_prog_spi_transaction(instance, AVR_ISP_COMMIT(addr));
/* polling flash */
if(data == 0xFF) {
furi_delay_ms(5);
} else {
/* polling flash */
uint32_t starttime = furi_get_tick();
while((furi_get_tick() - starttime) < 30) {
if(avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)) != 0xFF) {
break;
};
}
}
}
static uint16_t avr_isp_prog_current_page(AvrIspProg* instance) {
furi_assert(instance);
uint16_t page = 0;
switch(instance->cfg->pagesize) {
case 32:
page = instance->addr & 0xFFFFFFF0;
break;
case 64:
page = instance->addr & 0xFFFFFFE0;
break;
case 128:
page = instance->addr & 0xFFFFFFC0;
break;
case 256:
page = instance->addr & 0xFFFFFF80;
break;
default:
page = instance->addr;
break;
}
return page;
}
static uint8_t avr_isp_prog_write_flash_pages(AvrIspProg* instance, size_t length) {
furi_assert(instance);
size_t x = 0;
uint16_t page = avr_isp_prog_current_page(instance);
while(x < length) {
if(page != avr_isp_prog_current_page(instance)) {
--x;
avr_isp_prog_commit(instance, page, instance->buff[x++]);
page = avr_isp_prog_current_page(instance);
}
avr_isp_prog_spi_transaction(
instance, AVR_ISP_WRITE_FLASH_LO(instance->addr, instance->buff[x++]));
avr_isp_prog_spi_transaction(
instance, AVR_ISP_WRITE_FLASH_HI(instance->addr, instance->buff[x++]));
instance->addr++;
}
avr_isp_prog_commit(instance, page, instance->buff[--x]);
return STK_OK;
}
static void avr_isp_prog_write_flash(AvrIspProg* instance, size_t length) {
furi_assert(instance);
avr_isp_prog_fill(instance, length);
if(avr_isp_prog_getch(instance) == CRC_EOP) {
avr_isp_prog_tx_ch(instance, STK_INSYNC);
avr_isp_prog_tx_ch(instance, avr_isp_prog_write_flash_pages(instance, length));
} else {
instance->error++;
avr_isp_prog_tx_ch(instance, STK_NOSYNC);
}
}
// write (length) bytes, (start) is a byte address
static uint8_t
avr_isp_prog_write_eeprom_chunk(AvrIspProg* instance, uint16_t start, uint16_t length) {
furi_assert(instance);
// this writes byte-by-byte,
// page writing may be faster (4 bytes at a time)
avr_isp_prog_fill(instance, length);
for(uint16_t x = 0; x < length; x++) {
uint16_t addr = start + x;
avr_isp_prog_spi_transaction(instance, AVR_ISP_WRITE_EEPROM(addr, instance->buff[x]));
furi_delay_ms(10);
}
return STK_OK;
}
static uint8_t avr_isp_prog_write_eeprom(AvrIspProg* instance, size_t length) {
furi_assert(instance);
// here is a word address, get the byte address
uint16_t start = instance->addr * 2;
uint16_t remaining = length;
if(length > instance->cfg->eepromsize) {
instance->error++;
return STK_FAILED;
}
while(remaining > AVR_ISP_EECHUNK) {
avr_isp_prog_write_eeprom_chunk(instance, start, AVR_ISP_EECHUNK);
start += AVR_ISP_EECHUNK;
remaining -= AVR_ISP_EECHUNK;
}
avr_isp_prog_write_eeprom_chunk(instance, start, remaining);
return STK_OK;
}
static void avr_isp_prog_program_page(AvrIspProg* instance) {
furi_assert(instance);
uint8_t result = STK_FAILED;
uint16_t length = avr_isp_prog_getch(instance) << 8 | avr_isp_prog_getch(instance);
uint8_t memtype = avr_isp_prog_getch(instance);
// flash memory @addr, (length) bytes
if(memtype == STK_SET_FLASH_TYPE) {
avr_isp_prog_write_flash(instance, length);
return;
}
if(memtype == STK_SET_EEPROM_TYPE) {
result = avr_isp_prog_write_eeprom(instance, length);
if(avr_isp_prog_getch(instance) == CRC_EOP) {
avr_isp_prog_tx_ch(instance, STK_INSYNC);
avr_isp_prog_tx_ch(instance, result);
} else {
instance->error++;
avr_isp_prog_tx_ch(instance, STK_NOSYNC);
}
return;
}
avr_isp_prog_tx_ch(instance, STK_FAILED);
return;
}
static uint8_t avr_isp_prog_flash_read_page(AvrIspProg* instance, uint16_t length) {
furi_assert(instance);
for(uint16_t x = 0; x < length; x += 2) {
avr_isp_prog_tx_ch(
instance,
avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_FLASH_LO(instance->addr)));
avr_isp_prog_tx_ch(
instance,
avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(instance->addr)));
instance->addr++;
}
return STK_OK;
}
static uint8_t avr_isp_prog_eeprom_read_page(AvrIspProg* instance, uint16_t length) {
furi_assert(instance);
// here again we have a word address
uint16_t start = instance->addr * 2;
for(uint16_t x = 0; x < length; x++) {
uint16_t addr = start + x;
avr_isp_prog_tx_ch(
instance, avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_EEPROM(addr)));
}
return STK_OK;
}
static void avr_isp_prog_read_page(AvrIspProg* instance) {
furi_assert(instance);
uint8_t result = STK_FAILED;
uint16_t length = avr_isp_prog_getch(instance) << 8 | avr_isp_prog_getch(instance);
uint8_t memtype = avr_isp_prog_getch(instance);
if(avr_isp_prog_getch(instance) != CRC_EOP) {
instance->error++;
avr_isp_prog_tx_ch(instance, STK_NOSYNC);
return;
}
avr_isp_prog_tx_ch(instance, STK_INSYNC);
if(memtype == STK_SET_FLASH_TYPE) result = avr_isp_prog_flash_read_page(instance, length);
if(memtype == STK_SET_EEPROM_TYPE) result = avr_isp_prog_eeprom_read_page(instance, length);
avr_isp_prog_tx_ch(instance, result);
}
static void avr_isp_prog_read_signature(AvrIspProg* instance) {
furi_assert(instance);
if(avr_isp_prog_getch(instance) != CRC_EOP) {
instance->error++;
avr_isp_prog_tx_ch(instance, STK_NOSYNC);
return;
}
avr_isp_prog_tx_ch(instance, STK_INSYNC);
avr_isp_prog_tx_ch(instance, avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_VENDOR));
avr_isp_prog_tx_ch(instance, avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_PART_FAMILY));
avr_isp_prog_tx_ch(instance, avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_PART_NUMBER));
avr_isp_prog_tx_ch(instance, STK_OK);
}
void avr_isp_prog_avrisp(AvrIspProg* instance) {
furi_assert(instance);
uint8_t ch = avr_isp_prog_getch(instance);
switch(ch) {
case STK_GET_SYNC:
FURI_LOG_D(TAG, "cmd STK_GET_SYNC");
instance->error = 0;
avr_isp_prog_empty_reply(instance);
break;
case STK_GET_SIGN_ON:
FURI_LOG_D(TAG, "cmd STK_GET_SIGN_ON");
if(avr_isp_prog_getch(instance) == CRC_EOP) {
avr_isp_prog_tx_ch(instance, STK_INSYNC);
avr_isp_prog_tx_ch(instance, 'A');
avr_isp_prog_tx_ch(instance, 'V');
avr_isp_prog_tx_ch(instance, 'R');
avr_isp_prog_tx_ch(instance, ' ');
avr_isp_prog_tx_ch(instance, 'I');
avr_isp_prog_tx_ch(instance, 'S');
avr_isp_prog_tx_ch(instance, 'P');
avr_isp_prog_tx_ch(instance, STK_OK);
} else {
instance->error++;
avr_isp_prog_tx_ch(instance, STK_NOSYNC);
}
break;
case STK_GET_PARAMETER:
FURI_LOG_D(TAG, "cmd STK_GET_PARAMETER");
avr_isp_prog_get_version(instance, avr_isp_prog_getch(instance));
break;
case STK_SET_DEVICE:
FURI_LOG_D(TAG, "cmd STK_SET_DEVICE");
avr_isp_prog_fill(instance, 20);
avr_isp_prog_set_cfg(instance);
avr_isp_prog_empty_reply(instance);
break;
case STK_SET_DEVICE_EXT: // ignore for now
FURI_LOG_D(TAG, "cmd STK_SET_DEVICE_EXT");
avr_isp_prog_fill(instance, 5);
avr_isp_prog_empty_reply(instance);
break;
case STK_ENTER_PROGMODE:
FURI_LOG_D(TAG, "cmd STK_ENTER_PROGMODE");
if(!instance->pmode) avr_isp_prog_auto_set_spi_speed_start_pmode(instance);
avr_isp_prog_empty_reply(instance);
break;
case STK_LOAD_ADDRESS:
FURI_LOG_D(TAG, "cmd STK_LOAD_ADDRESS");
instance->addr = avr_isp_prog_getch(instance) | avr_isp_prog_getch(instance) << 8;
avr_isp_prog_empty_reply(instance);
break;
case STK_PROG_FLASH: // ignore for now
FURI_LOG_D(TAG, "cmd STK_PROG_FLASH");
avr_isp_prog_getch(instance);
avr_isp_prog_getch(instance);
avr_isp_prog_empty_reply(instance);
break;
case STK_PROG_DATA: // ignore for now
FURI_LOG_D(TAG, "cmd STK_PROG_DATA");
avr_isp_prog_getch(instance);
avr_isp_prog_empty_reply(instance);
break;
case STK_PROG_PAGE:
FURI_LOG_D(TAG, "cmd STK_PROG_PAGE");
avr_isp_prog_program_page(instance);
break;
case STK_READ_PAGE:
FURI_LOG_D(TAG, "cmd STK_READ_PAGE");
avr_isp_prog_read_page(instance);
break;
case STK_UNIVERSAL:
FURI_LOG_D(TAG, "cmd STK_UNIVERSAL");
avr_isp_prog_universal(instance);
break;
case STK_LEAVE_PROGMODE:
FURI_LOG_D(TAG, "cmd STK_LEAVE_PROGMODE");
instance->error = 0;
if(instance->pmode) avr_isp_prog_end_pmode(instance);
avr_isp_prog_empty_reply(instance);
break;
case STK_READ_SIGN:
FURI_LOG_D(TAG, "cmd STK_READ_SIGN");
avr_isp_prog_read_signature(instance);
break;
// expecting a command, not CRC_EOP
// this is how we can get back in sync
case CRC_EOP:
FURI_LOG_D(TAG, "cmd CRC_EOP");
instance->error++;
avr_isp_prog_tx_ch(instance, STK_NOSYNC);
break;
// anything else we will return STK_UNKNOWN
default:
FURI_LOG_D(TAG, "cmd STK_ERROR_CMD");
instance->error++;
if(avr_isp_prog_getch(instance) == CRC_EOP)
avr_isp_prog_tx_ch(instance, STK_UNKNOWN);
else
avr_isp_prog_tx_ch(instance, STK_NOSYNC);
}
if(instance->callback) {
instance->callback(instance->context);
}
}

View file

@ -1,16 +0,0 @@
#pragma once
#include "avr_isp_spi_sw.h"
#include <furi_hal.h>
typedef struct AvrIspProg AvrIspProg;
typedef void (*AvrIspProgCallback)(void* context);
AvrIspProg* avr_isp_prog_init(void);
void avr_isp_prog_free(AvrIspProg* instance);
size_t avr_isp_prog_spaces_rx(AvrIspProg* instance) ;
bool avr_isp_prog_rx(AvrIspProg* instance, uint8_t* data, size_t len);
size_t avr_isp_prog_tx(AvrIspProg* instance, uint8_t* data, size_t max_len);
void avr_isp_prog_avrisp(AvrIspProg* instance);
void avr_isp_prog_exit(AvrIspProg* instance);
void avr_isp_prog_set_tx_callback(AvrIspProg* instance, AvrIspProgCallback callback, void* context);

View file

@ -1,97 +0,0 @@
#pragma once
// http://ww1.microchip.com/downloads/en/appnotes/atmel-0943-in-system-programming_applicationnote_avr910.pdf
// AVR ISP Definitions
#define AVR_ISP_HWVER 0X02
#define AVR_ISP_SWMAJ 0X01
#define AVR_ISP_SWMIN 0X12
#define AVP_ISP_SERIAL_CONNECT_TYPE 0X53
#define AVP_ISP_CONNECT_TYPE 0x93
#define AVR_ISP_RESP_0 0X00
#define AVR_ISP_SET_PMODE 0xAC, 0x53, 0x00, 0x00
#define AVR_ISP_READ_VENDOR 0x30, 0x00, 0x00, 0x00
#define AVR_ISP_READ_PART_FAMILY 0x30, 0x00, 0x01, 0x00
#define AVR_ISP_READ_PART_NUMBER 0x30, 0x00, 0x02, 0x00
#define AVR_ISP_ERASE_CHIP \
0xAC, 0x80, 0x00, 0x00 //Erase Chip, Wait N ms, Release RESET to end the erase.
//The only way to end a Chip Erase cycle is by temporarily releasing the Reset line
#define AVR_ISP_EXTENDED_ADDR(data) 0x4D, 0x00, data, 0x00
#define AVR_ISP_WRITE_FLASH_LO(add, data) 0x40, (add >> 8) & 0xFF, add & 0xFF, data
#define AVR_ISP_WRITE_FLASH_HI(add, data) 0x48, (add >> 8) & 0xFF, add & 0xFF, data
#define AVR_ISP_READ_FLASH_LO(add) 0x20, (add >> 8) & 0xFF, add & 0xFF, 0x00
#define AVR_ISP_READ_FLASH_HI(add) 0x28, (add >> 8) & 0xFF, add & 0xFF, 0x00
#define AVR_ISP_WRITE_EEPROM(add, data) \
0xC0, (add >> 8) & 0xFF, add & 0xFF, data //Send cmd, Wait N ms
#define AVR_ISP_READ_EEPROM(add) 0xA0, (add >> 8) & 0xFF, add & 0xFF, 0xFF
#define AVR_ISP_COMMIT(add) \
0x4C, (add >> 8) & 0xFF, add & 0xFF, 0x00 //Send cmd, polling read last addr page
#define AVR_ISP_OSCCAL(add) 0x38, 0x00, add, 0x00
#define AVR_ISP_WRITE_LOCK_BYTE(data) 0xAC, 0xE0, 0x00, data //Send cmd, Wait N ms
#define AVR_ISP_READ_LOCK_BYTE 0x58, 0x00, 0x00, 0x00
#define AVR_ISP_WRITE_FUSE_LOW(data) 0xAC, 0xA0, 0x00, data //Send cmd, Wait N ms
#define AVR_ISP_READ_FUSE_LOW 0x50, 0x00, 0x00, 0x00
#define AVR_ISP_WRITE_FUSE_HIGH(data) 0xAC, 0xA8, 0x00, data //Send cmd, Wait N ms
#define AVR_ISP_READ_FUSE_HIGH 0x58, 0x08, 0x00, 0x00
#define AVR_ISP_WRITE_FUSE_EXTENDED(data) 0xAC, 0xA4, 0x00, data //Send cmd, Wait N ms (~write)
#define AVR_ISP_READ_FUSE_EXTENDED 0x50, 0x08, 0x00, 0x00
#define AVR_ISP_EECHUNK 0x20
// https://www.microchip.com/content/dam/mchp/documents/OTH/ApplicationNotes/ApplicationNotes/doc2525.pdf
// STK Definitions
#define STK_OK 0x10
#define STK_FAILED 0x11
#define STK_UNKNOWN 0x12
#define STK_INSYNC 0x14
#define STK_NOSYNC 0x15
#define CRC_EOP 0x20
#define STK_GET_SYNC 0x30
#define STK_GET_SIGN_ON 0x31
#define STK_SET_PARAMETER 0x40
#define STK_GET_PARAMETER 0x41
#define STK_SET_DEVICE 0x42
#define STK_SET_DEVICE_EXT 0x45
#define STK_ENTER_PROGMODE 0x50
#define STK_LEAVE_PROGMODE 0x51
#define STK_CHIP_ERASE 0x52
#define STK_CHECK_AUTOINC 0x53
#define STK_LOAD_ADDRESS 0x55
#define STK_UNIVERSAL 0x56
#define STK_UNIVERSAL_MULTI 0x57
#define STK_PROG_FLASH 0x60
#define STK_PROG_DATA 0x61
#define STK_PROG_FUSE 0x62
#define STK_PROG_FUSE_EXT 0x65
#define STK_PROG_LOCK 0x63
#define STK_PROG_PAGE 0x64
#define STK_READ_FLASH 0x70
#define STK_READ_DATA 0x71
#define STK_READ_FUSE 0x72
#define STK_READ_LOCK 0x73
#define STK_READ_PAGE 0x74
#define STK_READ_SIGN 0x75
#define STK_READ_OSCCAL 0x76
#define STK_READ_FUSE_EXT 0x77
#define STK_READ_OSCCAL_EXT 0x78
#define STK_HW_VER 0x80
#define STK_SW_MAJOR 0x81
#define STK_SW_MINOR 0x82
#define STK_LEDS 0x83
#define STK_VTARGET 0x84
#define STK_VADJUST 0x85
#define STK_OSC_PSCALE 0x86
#define STK_OSC_CMATCH 0x87
#define STK_SCK_DURATION 0x89
#define STK_BUFSIZEL 0x90
#define STK_BUFSIZEH 0x91
#define STK_STK500_TOPCARD_DETECT 0x98
#define STK_SET_EEPROM_TYPE 0X45
#define STK_SET_FLASH_TYPE 0X46

View file

@ -1,71 +0,0 @@
#include "avr_isp_spi_sw.h"
#include <furi.h>
#define AVR_ISP_SPI_SW_MISO &gpio_ext_pa6
#define AVR_ISP_SPI_SW_MOSI &gpio_ext_pa7
#define AVR_ISP_SPI_SW_SCK &gpio_ext_pb3
#define AVR_ISP_RESET &gpio_ext_pb2
struct AvrIspSpiSw {
AvrIspSpiSwSpeed speed_wait_time;
const GpioPin* miso;
const GpioPin* mosi;
const GpioPin* sck;
const GpioPin* res;
};
AvrIspSpiSw* avr_isp_spi_sw_init(AvrIspSpiSwSpeed speed) {
AvrIspSpiSw* instance = malloc(sizeof(AvrIspSpiSw));
instance->speed_wait_time = speed;
instance->miso = AVR_ISP_SPI_SW_MISO;
instance->mosi = AVR_ISP_SPI_SW_MOSI;
instance->sck = AVR_ISP_SPI_SW_SCK;
instance->res = AVR_ISP_RESET;
furi_hal_gpio_init(instance->miso, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_write(instance->mosi, false);
furi_hal_gpio_init(instance->mosi, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_write(instance->sck, false);
furi_hal_gpio_init(instance->sck, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
furi_hal_gpio_init(instance->res, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
return instance;
}
void avr_isp_spi_sw_free(AvrIspSpiSw* instance) {
furi_assert(instance);
furi_hal_gpio_init(instance->res, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
furi_hal_gpio_init(instance->miso, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
furi_hal_gpio_init(instance->mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
furi_hal_gpio_init(instance->sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
free(instance);
}
uint8_t avr_isp_spi_sw_txrx(AvrIspSpiSw* instance, uint8_t data) {
furi_assert(instance);
for(uint8_t i = 0; i < 8; ++i) {
furi_hal_gpio_write(instance->mosi, (data & 0x80) ? true : false);
furi_hal_gpio_write(instance->sck, true);
if(instance->speed_wait_time != AvrIspSpiSwSpeed1Mhz)
furi_delay_us(instance->speed_wait_time - 1);
data = (data << 1) | furi_hal_gpio_read(instance->miso); //-V792
furi_hal_gpio_write(instance->sck, false);
if(instance->speed_wait_time != AvrIspSpiSwSpeed1Mhz)
furi_delay_us(instance->speed_wait_time - 1);
}
return data;
}
void avr_isp_spi_sw_res_set(AvrIspSpiSw* instance, bool state) {
furi_assert(instance);
furi_hal_gpio_write(instance->res, state);
}
void avr_isp_spi_sw_sck_set(AvrIspSpiSw* instance, bool state) {
furi_assert(instance);
furi_hal_gpio_write(instance->sck, state);
}

View file

@ -1,24 +0,0 @@
#pragma once
#include <furi_hal.h>
typedef enum {
AvrIspSpiSwSpeed1Mhz = 0,
AvrIspSpiSwSpeed400Khz = 1,
AvrIspSpiSwSpeed250Khz = 2,
AvrIspSpiSwSpeed125Khz = 4,
AvrIspSpiSwSpeed60Khz = 8,
AvrIspSpiSwSpeed40Khz = 12,
AvrIspSpiSwSpeed20Khz = 24,
AvrIspSpiSwSpeed10Khz = 48,
AvrIspSpiSwSpeed5Khz = 96,
AvrIspSpiSwSpeed1Khz = 480,
} AvrIspSpiSwSpeed;
typedef struct AvrIspSpiSw AvrIspSpiSw;
AvrIspSpiSw* avr_isp_spi_sw_init(AvrIspSpiSwSpeed speed);
void avr_isp_spi_sw_free(AvrIspSpiSw* instance);
uint8_t avr_isp_spi_sw_txrx(AvrIspSpiSw* instance, uint8_t data);
void avr_isp_spi_sw_res_set(AvrIspSpiSw* instance, bool state);
void avr_isp_spi_sw_sck_set(AvrIspSpiSw* instance, bool state);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

View file

@ -1,30 +0,0 @@
#include "../avr_isp_app_i.h"
// Generate scene on_enter handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const avr_isp_scene_on_enter_handlers[])(void*) = {
#include "avr_isp_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_event handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
bool (*const avr_isp_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
#include "avr_isp_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_exit handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
void (*const avr_isp_scene_on_exit_handlers[])(void* context) = {
#include "avr_isp_scene_config.h"
};
#undef ADD_SCENE
// Initialize scene handlers configuration structure
const SceneManagerHandlers avr_isp_scene_handlers = {
.on_enter_handlers = avr_isp_scene_on_enter_handlers,
.on_event_handlers = avr_isp_scene_on_event_handlers,
.on_exit_handlers = avr_isp_scene_on_exit_handlers,
.scene_num = AvrIspSceneNum,
};

View file

@ -1,29 +0,0 @@
#pragma once
#include <gui/scene_manager.h>
// Generate scene id and total number
#define ADD_SCENE(prefix, name, id) AvrIspScene##id,
typedef enum {
#include "avr_isp_scene_config.h"
AvrIspSceneNum,
} AvrIspScene;
#undef ADD_SCENE
extern const SceneManagerHandlers avr_isp_scene_handlers;
// Generate scene on_enter handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
#include "avr_isp_scene_config.h"
#undef ADD_SCENE
// Generate scene on_event handlers declaration
#define ADD_SCENE(prefix, name, id) \
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
#include "avr_isp_scene_config.h"
#undef ADD_SCENE
// Generate scene on_exit handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
#include "avr_isp_scene_config.h"
#undef ADD_SCENE

View file

@ -1,99 +0,0 @@
#include "../avr_isp_app_i.h"
#include "../helpers/avr_isp_types.h"
void avr_isp_scene_about_widget_callback(GuiButtonType result, InputType type, void* context) {
furi_assert(context);
AvrIspApp* app = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
}
void avr_isp_scene_about_on_enter(void* context) {
furi_assert(context);
AvrIspApp* app = context;
FuriString* temp_str = furi_string_alloc();
furi_string_printf(temp_str, "\e#%s\n", "Information");
furi_string_cat_printf(temp_str, "Version: %s\n", AVR_ISP_VERSION_APP);
furi_string_cat_printf(temp_str, "Developed by: %s\n", AVR_ISP_DEVELOPED);
furi_string_cat_printf(temp_str, "Github: %s\n\n", AVR_ISP_GITHUB);
furi_string_cat_printf(temp_str, "\e#%s\n", "Description");
furi_string_cat_printf(
temp_str,
"This application is an AVR in-system programmer based on stk500mk1. It is compatible with AVR-based"
" microcontrollers including Arduino. You can also use it to repair the chip if you accidentally"
" corrupt the bootloader.\n\n");
furi_string_cat_printf(temp_str, "\e#%s\n", "What it can do:");
furi_string_cat_printf(temp_str, "- Create a dump of your chip on an SD card\n");
furi_string_cat_printf(temp_str, "- Flash your chip firmware from the SD card\n");
furi_string_cat_printf(temp_str, "- Act as a wired USB ISP using avrdude software\n\n");
furi_string_cat_printf(temp_str, "\e#%s\n", "Supported chip series:");
furi_string_cat_printf(
temp_str,
"Example command for avrdude flashing: avrdude.exe -p m328p -c stk500v1 -P COMxx -U flash:r:"
"X:\\sketch_sample.hex"
":i\n");
furi_string_cat_printf(
temp_str,
"Where: "
"-p m328p"
" brand of your chip, "
"-P COMxx"
" com port number in the system when "
"ISP Programmer"
" is enabled\n\n");
furi_string_cat_printf(temp_str, "\e#%s\n", "Info");
furi_string_cat_printf(
temp_str,
"ATtinyXXXX\nATmegaXXXX\nAT43Uxxx\nAT76C711\nAT86RF401\nAT90xxxxx\nAT94K\n"
"ATAxxxxx\nATA664251\nM3000\nLGT8F88P\nLGT8F168P\nLGT8F328P\n");
furi_string_cat_printf(
temp_str, "For a more detailed list of supported chips, see AVRDude help\n");
widget_add_text_box_element(
app->widget,
0,
0,
128,
14,
AlignCenter,
AlignBottom,
"\e#\e! \e!\n",
false);
widget_add_text_box_element(
app->widget,
0,
2,
128,
14,
AlignCenter,
AlignBottom,
"\e#\e! ISP Programmer \e!\n",
false);
widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str));
furi_string_free(temp_str);
view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewWidget);
}
bool avr_isp_scene_about_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void avr_isp_scene_about_on_exit(void* context) {
furi_assert(context);
AvrIspApp* app = context;
// Clear views
widget_reset(app->widget);
}

View file

@ -1,72 +0,0 @@
#include "../avr_isp_app_i.h"
void avr_isp_scene_chip_detect_callback(AvrIspCustomEvent event, void* context) {
furi_assert(context);
AvrIspApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
void avr_isp_scene_chip_detect_on_enter(void* context) {
furi_assert(context);
AvrIspApp* app = context;
switch(app->error) {
case AvrIspErrorReading:
case AvrIspErrorWriting:
case AvrIspErrorWritingFuse:
avr_isp_chip_detect_set_state(
app->avr_isp_chip_detect_view, AvrIspChipDetectViewStateErrorOccured);
break;
case AvrIspErrorVerification:
avr_isp_chip_detect_set_state(
app->avr_isp_chip_detect_view, AvrIspChipDetectViewStateErrorVerification);
break;
default:
avr_isp_chip_detect_set_state(
app->avr_isp_chip_detect_view, AvrIspChipDetectViewStateNoDetect);
break;
}
app->error = AvrIspErrorNoError;
avr_isp_chip_detect_view_set_callback(
app->avr_isp_chip_detect_view, avr_isp_scene_chip_detect_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewChipDetect);
}
bool avr_isp_scene_chip_detect_on_event(void* context, SceneManagerEvent event) {
furi_assert(context);
AvrIspApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case AvrIspCustomEventSceneChipDetectOk:
if(scene_manager_get_scene_state(app->scene_manager, AvrIspSceneChipDetect) ==
AvrIspViewProgrammer) {
scene_manager_next_scene(app->scene_manager, AvrIspSceneProgrammer);
} else if(
scene_manager_get_scene_state(app->scene_manager, AvrIspSceneChipDetect) ==
AvrIspViewReader) {
scene_manager_next_scene(app->scene_manager, AvrIspSceneInputName);
} else if(
scene_manager_get_scene_state(app->scene_manager, AvrIspSceneChipDetect) ==
AvrIspViewWriter) {
scene_manager_next_scene(app->scene_manager, AvrIspSceneLoad);
}
consumed = true;
break;
default:
break;
}
} else if(event.type == SceneManagerEventTypeTick) {
}
return consumed;
}
void avr_isp_scene_chip_detect_on_exit(void* context) {
UNUSED(context);
}

View file

@ -1,10 +0,0 @@
ADD_SCENE(avr_isp, start, Start)
ADD_SCENE(avr_isp, about, About)
ADD_SCENE(avr_isp, programmer, Programmer)
ADD_SCENE(avr_isp, reader, Reader)
ADD_SCENE(avr_isp, input_name, InputName)
ADD_SCENE(avr_isp, load, Load)
ADD_SCENE(avr_isp, writer, Writer)
ADD_SCENE(avr_isp, wiring, Wiring)
ADD_SCENE(avr_isp, chip_detect, ChipDetect)
ADD_SCENE(avr_isp, success, Success)

View file

@ -1,89 +0,0 @@
#include "../avr_isp_app_i.h"
#include <gui/modules/validators.h>
#define MAX_TEXT_INPUT_LEN 22
void avr_isp_scene_input_name_text_callback(void* context) {
furi_assert(context);
AvrIspApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, AvrIspCustomEventSceneInputName);
}
void avr_isp_scene_input_name_get_timefilename(FuriString* name) {
FuriHalRtcDateTime datetime = {0};
furi_hal_rtc_get_datetime(&datetime);
furi_string_printf(
name,
"AVR_dump-%.4d%.2d%.2d-%.2d%.2d%.2d",
datetime.year,
datetime.month,
datetime.day,
datetime.hour,
datetime.minute,
datetime.second);
}
void avr_isp_scene_input_name_on_enter(void* context) {
furi_assert(context);
AvrIspApp* app = context;
// Setup view
TextInput* text_input = app->text_input;
bool dev_name_empty = false;
FuriString* file_name = furi_string_alloc();
avr_isp_scene_input_name_get_timefilename(file_name);
furi_string_set(app->file_path, STORAGE_APP_DATA_PATH_PREFIX);
//highlighting the entire filename by default
dev_name_empty = true;
strncpy(app->file_name_tmp, furi_string_get_cstr(file_name), AVR_ISP_MAX_LEN_NAME);
text_input_set_header_text(text_input, "Name dump");
text_input_set_result_callback(
text_input,
avr_isp_scene_input_name_text_callback,
app,
app->file_name_tmp,
MAX_TEXT_INPUT_LEN, // buffer size
dev_name_empty);
ValidatorIsFile* validator_is_file =
validator_is_file_alloc_init(STORAGE_APP_DATA_PATH_PREFIX, AVR_ISP_APP_EXTENSION, "");
text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
furi_string_free(file_name);
view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewTextInput);
}
bool avr_isp_scene_input_name_on_event(void* context, SceneManagerEvent event) {
furi_assert(context);
AvrIspApp* app = context;
if(event.type == SceneManagerEventTypeBack) {
scene_manager_previous_scene(app->scene_manager);
return true;
} else if(event.type == SceneManagerEventTypeCustom) {
if(event.event == AvrIspCustomEventSceneInputName) {
if(strcmp(app->file_name_tmp, "") != 0) {
scene_manager_next_scene(app->scene_manager, AvrIspSceneReader);
} else {
}
}
}
return false;
}
void avr_isp_scene_input_name_on_exit(void* context) {
furi_assert(context);
AvrIspApp* app = context;
// Clear validator
void* validator_context = text_input_get_validator_callback_context(app->text_input);
text_input_set_validator(app->text_input, NULL, NULL);
validator_is_file_free(validator_context);
// Clear view
text_input_reset(app->text_input);
}

View file

@ -1,22 +0,0 @@
#include "../avr_isp_app_i.h"
void avr_isp_scene_load_on_enter(void* context) {
furi_assert(context);
AvrIspApp* app = context;
if(avr_isp_load_from_file(app)) {
scene_manager_next_scene(app->scene_manager, AvrIspSceneWriter);
} else {
scene_manager_previous_scene(app->scene_manager);
}
}
bool avr_isp_scene_load_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void avr_isp_scene_load_on_exit(void* context) {
UNUSED(context);
}

View file

@ -1,28 +0,0 @@
#include "../avr_isp_app_i.h"
void avr_isp_scene_programmer_callback(AvrIspCustomEvent event, void* context) {
furi_assert(context);
AvrIspApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
void avr_isp_scene_programmer_on_enter(void* context) {
furi_assert(context);
AvrIspApp* app = context;
avr_isp_programmer_view_set_callback(
app->avr_isp_programmer_view, avr_isp_scene_programmer_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewProgrammer);
}
bool avr_isp_scene_programmer_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void avr_isp_scene_programmer_on_exit(void* context) {
UNUSED(context);
}

View file

@ -1,64 +0,0 @@
#include "../avr_isp_app_i.h"
void avr_isp_scene_reader_callback(AvrIspCustomEvent event, void* context) {
furi_assert(context);
AvrIspApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
void avr_isp_scene_reader_on_enter(void* context) {
furi_assert(context);
AvrIspApp* app = context;
avr_isp_reader_set_file_path(
app->avr_isp_reader_view, furi_string_get_cstr(app->file_path), app->file_name_tmp);
avr_isp_reader_view_set_callback(app->avr_isp_reader_view, avr_isp_scene_reader_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewReader);
}
bool avr_isp_scene_reader_on_event(void* context, SceneManagerEvent event) {
furi_assert(context);
AvrIspApp* app = context;
UNUSED(app);
bool consumed = false;
if(event.type == SceneManagerEventTypeBack) {
//do not handle exit on "Back"
consumed = true;
} else if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case AvrIspCustomEventSceneReadingOk:
scene_manager_next_scene(app->scene_manager, AvrIspSceneSuccess);
consumed = true;
break;
case AvrIspCustomEventSceneExit:
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, AvrIspSceneChipDetect);
consumed = true;
break;
case AvrIspCustomEventSceneErrorVerification:
app->error = AvrIspErrorVerification;
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, AvrIspSceneChipDetect);
consumed = true;
break;
case AvrIspCustomEventSceneErrorReading:
app->error = AvrIspErrorReading;
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, AvrIspSceneChipDetect);
consumed = true;
break;
default:
break;
}
} else if(event.type == SceneManagerEventTypeTick) {
avr_isp_reader_update_progress(app->avr_isp_reader_view);
}
return consumed;
}
void avr_isp_scene_reader_on_exit(void* context) {
UNUSED(context);
}

View file

@ -1,75 +0,0 @@
#include "../avr_isp_app_i.h"
void avr_isp_scene_start_submenu_callback(void* context, uint32_t index) {
furi_assert(context);
AvrIspApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void avr_isp_scene_start_on_enter(void* context) {
furi_assert(context);
AvrIspApp* app = context;
Submenu* submenu = app->submenu;
submenu_add_item(
submenu, "Dump AVR", SubmenuIndexAvrIspReader, avr_isp_scene_start_submenu_callback, app);
submenu_add_item(
submenu, "Flash AVR", SubmenuIndexAvrIspWriter, avr_isp_scene_start_submenu_callback, app);
submenu_add_item(
submenu,
"ISP Programmer",
SubmenuIndexAvrIspProgrammer,
avr_isp_scene_start_submenu_callback,
app);
submenu_add_item(
submenu, "Wiring", SubmenuIndexAvrIsWiring, avr_isp_scene_start_submenu_callback, app);
submenu_add_item(
submenu, "About", SubmenuIndexAvrIspAbout, avr_isp_scene_start_submenu_callback, app);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(app->scene_manager, AvrIspSceneStart));
view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewSubmenu);
}
bool avr_isp_scene_start_on_event(void* context, SceneManagerEvent event) {
furi_assert(context);
AvrIspApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubmenuIndexAvrIspAbout) {
scene_manager_next_scene(app->scene_manager, AvrIspSceneAbout);
consumed = true;
} else if(event.event == SubmenuIndexAvrIspProgrammer) {
scene_manager_set_scene_state(
app->scene_manager, AvrIspSceneChipDetect, AvrIspViewProgrammer);
scene_manager_next_scene(app->scene_manager, AvrIspSceneChipDetect);
consumed = true;
} else if(event.event == SubmenuIndexAvrIspReader) {
scene_manager_set_scene_state(
app->scene_manager, AvrIspSceneChipDetect, AvrIspViewReader);
scene_manager_next_scene(app->scene_manager, AvrIspSceneChipDetect);
consumed = true;
} else if(event.event == SubmenuIndexAvrIspWriter) {
scene_manager_set_scene_state(
app->scene_manager, AvrIspSceneChipDetect, AvrIspViewWriter);
scene_manager_next_scene(app->scene_manager, AvrIspSceneChipDetect);
consumed = true;
} else if(event.event == SubmenuIndexAvrIsWiring) {
scene_manager_next_scene(app->scene_manager, AvrIspSceneWiring);
consumed = true;
}
scene_manager_set_scene_state(app->scene_manager, AvrIspSceneStart, event.event);
}
return consumed;
}
void avr_isp_scene_start_on_exit(void* context) {
furi_assert(context);
AvrIspApp* app = context;
submenu_reset(app->submenu);
}

View file

@ -1,44 +0,0 @@
#include "../avr_isp_app_i.h"
void avr_isp_scene_success_popup_callback(void* context) {
furi_assert(context);
AvrIspApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, AvrIspCustomEventSceneSuccess);
}
void avr_isp_scene_success_on_enter(void* context) {
furi_assert(context);
AvrIspApp* app = context;
Popup* popup = app->popup;
popup_set_icon(popup, 32, 5, &I_dolphin_nice_96x59);
popup_set_header(popup, "Success!", 8, 22, AlignLeft, AlignBottom);
popup_set_timeout(popup, 1500);
popup_set_context(popup, app);
popup_set_callback(popup, avr_isp_scene_success_popup_callback);
popup_enable_timeout(popup);
view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewPopup);
}
bool avr_isp_scene_success_on_event(void* context, SceneManagerEvent event) {
furi_assert(context);
AvrIspApp* app = context;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == AvrIspCustomEventSceneSuccess) {
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, AvrIspSceneStart);
return true;
}
}
return false;
}
void avr_isp_scene_success_on_exit(void* context) {
furi_assert(context);
AvrIspApp* app = context;
Popup* popup = app->popup;
popup_reset(popup);
}

View file

@ -1,21 +0,0 @@
#include "../avr_isp_app_i.h"
void avr_isp_scene_wiring_on_enter(void* context) {
furi_assert(context);
AvrIspApp* app = context;
widget_add_icon_element(app->widget, 0, 0, &I_avr_wiring);
view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewWidget);
}
bool avr_isp_scene_wiring_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void avr_isp_scene_wiring_on_exit(void* context) {
furi_assert(context);
AvrIspApp* app = context;
widget_reset(app->widget);
}

View file

@ -1,69 +0,0 @@
#include "../avr_isp_app_i.h"
void avr_isp_scene_writer_callback(AvrIspCustomEvent event, void* context) {
furi_assert(context);
AvrIspApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, event);
}
void avr_isp_scene_writer_on_enter(void* context) {
furi_assert(context);
AvrIspApp* app = context;
avr_isp_writer_set_file_path(
app->avr_isp_writer_view, furi_string_get_cstr(app->file_path), app->file_name_tmp);
avr_isp_writer_view_set_callback(app->avr_isp_writer_view, avr_isp_scene_writer_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewWriter);
}
bool avr_isp_scene_writer_on_event(void* context, SceneManagerEvent event) {
furi_assert(context);
AvrIspApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeBack) {
//do not handle exit on "Back"
consumed = true;
} else if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case AvrIspCustomEventSceneExitStartMenu:
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, AvrIspSceneStart);
consumed = true;
break;
case AvrIspCustomEventSceneExit:
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, AvrIspSceneChipDetect);
consumed = true;
break;
case AvrIspCustomEventSceneErrorVerification:
app->error = AvrIspErrorVerification;
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, AvrIspSceneChipDetect);
consumed = true;
break;
case AvrIspCustomEventSceneErrorWriting:
app->error = AvrIspErrorWriting;
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, AvrIspSceneChipDetect);
consumed = true;
break;
case AvrIspCustomEventSceneErrorWritingFuse:
app->error = AvrIspErrorWritingFuse;
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, AvrIspSceneChipDetect);
consumed = true;
break;
default:
break;
}
} else if(event.type == SceneManagerEventTypeTick) {
avr_isp_writer_update_progress(app->avr_isp_writer_view);
}
return consumed;
}
void avr_isp_scene_writer_on_exit(void* context) {
UNUSED(context);
}

View file

@ -1,213 +0,0 @@
#include "avr_isp_view_chip_detect.h"
#include <avr_isp_icons.h>
#include <gui/elements.h>
#include "../helpers/avr_isp_worker_rw.h"
struct AvrIspChipDetectView {
View* view;
AvrIspWorkerRW* avr_isp_worker_rw;
AvrIspChipDetectViewCallback callback;
void* context;
};
typedef struct {
uint16_t idx;
const char* name_chip;
uint32_t flash_size;
AvrIspChipDetectViewState state;
} AvrIspChipDetectViewModel;
void avr_isp_chip_detect_view_set_callback(
AvrIspChipDetectView* instance,
AvrIspChipDetectViewCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
void avr_isp_chip_detect_set_state(AvrIspChipDetectView* instance, AvrIspChipDetectViewState state) {
furi_assert(instance);
with_view_model(
instance->view, AvrIspChipDetectViewModel * model, { model->state = state; }, true);
}
void avr_isp_chip_detect_view_draw(Canvas* canvas, AvrIspChipDetectViewModel* model) {
canvas_clear(canvas);
char str_buf[64] = {0};
canvas_set_font(canvas, FontPrimary);
switch(model->state) {
case AvrIspChipDetectViewStateDetected:
canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "AVR chip detected!");
canvas_draw_icon(canvas, 29, 14, &I_chip_long_70x22);
canvas_set_font(canvas, FontSecondary);
snprintf(str_buf, sizeof(str_buf), "%ld Kb", model->flash_size / 1024);
canvas_draw_str_aligned(canvas, 64, 25, AlignCenter, AlignCenter, str_buf);
canvas_draw_str_aligned(canvas, 64, 45, AlignCenter, AlignCenter, model->name_chip);
elements_button_right(canvas, "Next");
break;
case AvrIspChipDetectViewStateErrorOccured:
canvas_draw_str_aligned(
canvas, 64, 5, AlignCenter, AlignCenter, "Error occured, try again!");
canvas_draw_icon(canvas, 29, 14, &I_chip_error_70x22);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(
canvas, 64, 45, AlignCenter, AlignCenter, "Check the wiring and retry");
break;
case AvrIspChipDetectViewStateErrorVerification:
canvas_draw_str_aligned(
canvas, 64, 5, AlignCenter, AlignCenter, "Data verification failed");
canvas_draw_icon(canvas, 29, 14, &I_chip_error_70x22);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(
canvas, 64, 45, AlignCenter, AlignCenter, "Try to restart the process");
break;
default:
//AvrIspChipDetectViewStateNoDetect
canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "AVR chip not found!");
canvas_draw_icon(canvas, 29, 12, &I_chif_not_found_83x37);
break;
}
canvas_set_font(canvas, FontSecondary);
elements_button_left(canvas, "Retry");
}
bool avr_isp_chip_detect_view_input(InputEvent* event, void* context) {
furi_assert(context);
AvrIspChipDetectView* instance = context;
if(event->type == InputTypeShort) {
if(event->key == InputKeyBack) {
return false;
} else if(event->key == InputKeyRight) {
with_view_model(
instance->view,
AvrIspChipDetectViewModel * model,
{
if(model->state == AvrIspChipDetectViewStateDetected) {
if(instance->callback)
instance->callback(
AvrIspCustomEventSceneChipDetectOk, instance->context);
}
},
false);
} else if(event->key == InputKeyLeft) {
bool detect_chip = false;
with_view_model(
instance->view,
AvrIspChipDetectViewModel * model,
{
if(model->state != AvrIspChipDetectViewStateDetecting) {
model->state = AvrIspChipDetectViewStateDetecting;
detect_chip = true;
}
},
false);
if(detect_chip) avr_isp_worker_rw_detect_chip(instance->avr_isp_worker_rw);
}
} else {
return false;
}
return true;
}
static void avr_isp_chip_detect_detect_chip_callback(
void* context,
const char* name,
bool detect_chip,
uint32_t flash_size) {
furi_assert(context);
AvrIspChipDetectView* instance = context;
with_view_model(
instance->view,
AvrIspChipDetectViewModel * model,
{
model->name_chip = name;
model->flash_size = flash_size;
if(detect_chip) {
model->state = AvrIspChipDetectViewStateDetected;
} else {
model->state = AvrIspChipDetectViewStateNoDetect;
}
},
true);
}
void avr_isp_chip_detect_view_enter(void* context) {
furi_assert(context);
AvrIspChipDetectView* instance = context;
bool detect_chip = false;
with_view_model(
instance->view,
AvrIspChipDetectViewModel * model,
{
if(model->state == AvrIspChipDetectViewStateNoDetect ||
model->state == AvrIspChipDetectViewStateDetected) {
detect_chip = true;
}
},
false);
//Start avr_isp_worker_rw
instance->avr_isp_worker_rw = avr_isp_worker_rw_alloc(instance->context);
avr_isp_worker_rw_set_callback(
instance->avr_isp_worker_rw, avr_isp_chip_detect_detect_chip_callback, instance);
if(detect_chip) avr_isp_worker_rw_detect_chip(instance->avr_isp_worker_rw);
}
void avr_isp_chip_detect_view_exit(void* context) {
furi_assert(context);
AvrIspChipDetectView* instance = context;
avr_isp_worker_rw_set_callback(instance->avr_isp_worker_rw, NULL, NULL);
avr_isp_worker_rw_free(instance->avr_isp_worker_rw);
}
AvrIspChipDetectView* avr_isp_chip_detect_view_alloc() {
AvrIspChipDetectView* instance = malloc(sizeof(AvrIspChipDetectView));
// View allocation and configuration
instance->view = view_alloc();
view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspChipDetectViewModel));
view_set_context(instance->view, instance);
view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_chip_detect_view_draw);
view_set_input_callback(instance->view, avr_isp_chip_detect_view_input);
view_set_enter_callback(instance->view, avr_isp_chip_detect_view_enter);
view_set_exit_callback(instance->view, avr_isp_chip_detect_view_exit);
with_view_model(
instance->view,
AvrIspChipDetectViewModel * model,
{ model->state = AvrIspChipDetectViewStateNoDetect; },
false);
return instance;
}
void avr_isp_chip_detect_view_free(AvrIspChipDetectView* instance) {
furi_assert(instance);
view_free(instance->view);
free(instance);
}
View* avr_isp_chip_detect_view_get_view(AvrIspChipDetectView* instance) {
furi_assert(instance);
return instance->view;
}

View file

@ -1,32 +0,0 @@
#pragma once
#include <gui/view.h>
#include "../helpers/avr_isp_types.h"
#include "../helpers/avr_isp_event.h"
typedef struct AvrIspChipDetectView AvrIspChipDetectView;
typedef void (*AvrIspChipDetectViewCallback)(AvrIspCustomEvent event, void* context);
typedef enum {
AvrIspChipDetectViewStateNoDetect,
AvrIspChipDetectViewStateDetecting,
AvrIspChipDetectViewStateDetected,
AvrIspChipDetectViewStateErrorOccured,
AvrIspChipDetectViewStateErrorVerification,
} AvrIspChipDetectViewState;
void avr_isp_chip_detect_view_set_callback(
AvrIspChipDetectView* instance,
AvrIspChipDetectViewCallback callback,
void* context);
void avr_isp_chip_detect_set_state(AvrIspChipDetectView* instance, AvrIspChipDetectViewState state);
AvrIspChipDetectView* avr_isp_chip_detect_view_alloc();
void avr_isp_chip_detect_view_free(AvrIspChipDetectView* instance);
View* avr_isp_chip_detect_view_get_view(AvrIspChipDetectView* instance);
void avr_isp_chip_detect_view_exit(void* context);

View file

@ -1,134 +0,0 @@
#include "avr_isp_view_programmer.h"
#include <avr_isp_icons.h>
#include "../helpers/avr_isp_worker.h"
#include <gui/elements.h>
struct AvrIspProgrammerView {
View* view;
AvrIspWorker* worker;
AvrIspProgrammerViewCallback callback;
void* context;
};
typedef struct {
AvrIspProgrammerViewStatus status;
} AvrIspProgrammerViewModel;
void avr_isp_programmer_view_set_callback(
AvrIspProgrammerView* instance,
AvrIspProgrammerViewCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
void avr_isp_programmer_view_draw(Canvas* canvas, AvrIspProgrammerViewModel* model) {
canvas_clear(canvas);
if(model->status == AvrIspProgrammerViewStatusUSBConnect) {
canvas_set_font(canvas, FontPrimary);
canvas_draw_icon(canvas, 0, 0, &I_isp_active_128x53);
elements_multiline_text(canvas, 45, 10, "ISP mode active");
} else {
canvas_set_font(canvas, FontSecondary);
canvas_draw_icon(canvas, 51, 6, &I_link_waiting_77x56);
elements_multiline_text(canvas, 0, 25, "Waiting for\nsoftware\nconnection");
}
}
bool avr_isp_programmer_view_input(InputEvent* event, void* context) {
furi_assert(context);
UNUSED(context);
if(event->key == InputKeyBack || event->type != InputTypeShort) {
return false;
}
return true;
}
static void avr_isp_programmer_usb_connect_callback(void* context, bool status_connect) {
furi_assert(context);
AvrIspProgrammerView* instance = context;
with_view_model(
instance->view,
AvrIspProgrammerViewModel * model,
{
if(status_connect) {
model->status = AvrIspProgrammerViewStatusUSBConnect;
} else {
model->status = AvrIspProgrammerViewStatusNoUSBConnect;
}
},
true);
}
void avr_isp_programmer_view_enter(void* context) {
furi_assert(context);
AvrIspProgrammerView* instance = context;
with_view_model(
instance->view,
AvrIspProgrammerViewModel * model,
{ model->status = AvrIspProgrammerViewStatusNoUSBConnect; },
true);
//Start worker
instance->worker = avr_isp_worker_alloc(instance->context);
avr_isp_worker_set_callback(
instance->worker, avr_isp_programmer_usb_connect_callback, instance);
avr_isp_worker_start(instance->worker);
}
void avr_isp_programmer_view_exit(void* context) {
furi_assert(context);
AvrIspProgrammerView* instance = context;
//Stop worker
if(avr_isp_worker_is_running(instance->worker)) {
avr_isp_worker_stop(instance->worker);
}
avr_isp_worker_set_callback(instance->worker, NULL, NULL);
avr_isp_worker_free(instance->worker);
}
AvrIspProgrammerView* avr_isp_programmer_view_alloc() {
AvrIspProgrammerView* instance = malloc(sizeof(AvrIspProgrammerView));
// View allocation and configuration
instance->view = view_alloc();
view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspProgrammerViewModel));
view_set_context(instance->view, instance);
view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_programmer_view_draw);
view_set_input_callback(instance->view, avr_isp_programmer_view_input);
view_set_enter_callback(instance->view, avr_isp_programmer_view_enter);
view_set_exit_callback(instance->view, avr_isp_programmer_view_exit);
with_view_model(
instance->view,
AvrIspProgrammerViewModel * model,
{ model->status = AvrIspProgrammerViewStatusNoUSBConnect; },
false);
return instance;
}
void avr_isp_programmer_view_free(AvrIspProgrammerView* instance) {
furi_assert(instance);
view_free(instance->view);
free(instance);
}
View* avr_isp_programmer_view_get_view(AvrIspProgrammerView* instance) {
furi_assert(instance);
return instance->view;
}

View file

@ -1,27 +0,0 @@
#pragma once
#include <gui/view.h>
#include "../helpers/avr_isp_types.h"
#include "../helpers/avr_isp_event.h"
typedef struct AvrIspProgrammerView AvrIspProgrammerView;
typedef void (*AvrIspProgrammerViewCallback)(AvrIspCustomEvent event, void* context);
typedef enum {
AvrIspProgrammerViewStatusNoUSBConnect,
AvrIspProgrammerViewStatusUSBConnect,
} AvrIspProgrammerViewStatus;
void avr_isp_programmer_view_set_callback(
AvrIspProgrammerView* instance,
AvrIspProgrammerViewCallback callback,
void* context);
AvrIspProgrammerView* avr_isp_programmer_view_alloc();
void avr_isp_programmer_view_free(AvrIspProgrammerView* instance);
View* avr_isp_programmer_view_get_view(AvrIspProgrammerView* instance);
void avr_isp_programmer_view_exit(void* context);

View file

@ -1,215 +0,0 @@
#include "avr_isp_view_reader.h"
#include <gui/elements.h>
#include "../helpers/avr_isp_worker_rw.h"
struct AvrIspReaderView {
View* view;
AvrIspWorkerRW* avr_isp_worker_rw;
const char* file_path;
const char* file_name;
AvrIspReaderViewCallback callback;
void* context;
};
typedef struct {
AvrIspReaderViewStatus status;
float progress_flash;
float progress_eeprom;
} AvrIspReaderViewModel;
void avr_isp_reader_update_progress(AvrIspReaderView* instance) {
with_view_model(
instance->view,
AvrIspReaderViewModel * model,
{
model->progress_flash =
avr_isp_worker_rw_get_progress_flash(instance->avr_isp_worker_rw);
model->progress_eeprom =
avr_isp_worker_rw_get_progress_eeprom(instance->avr_isp_worker_rw);
},
true);
}
void avr_isp_reader_view_set_callback(
AvrIspReaderView* instance,
AvrIspReaderViewCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
void avr_isp_reader_set_file_path(
AvrIspReaderView* instance,
const char* file_path,
const char* file_name) {
furi_assert(instance);
instance->file_path = file_path;
instance->file_name = file_name;
}
void avr_isp_reader_view_draw(Canvas* canvas, AvrIspReaderViewModel* model) {
canvas_clear(canvas);
char str_buf[64] = {0};
canvas_set_font(canvas, FontPrimary);
switch(model->status) {
case AvrIspReaderViewStatusIDLE:
canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Press start to dump");
canvas_set_font(canvas, FontSecondary);
elements_button_center(canvas, "Start");
break;
case AvrIspReaderViewStatusReading:
canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Reading dump");
break;
case AvrIspReaderViewStatusVerification:
canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Verifyng dump");
break;
default:
break;
}
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 0, 27, "Flash");
snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_flash * 100));
elements_progress_bar_with_text(canvas, 44, 17, 84, model->progress_flash, str_buf);
canvas_draw_str(canvas, 0, 43, "EEPROM");
snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_eeprom * 100));
elements_progress_bar_with_text(canvas, 44, 34, 84, model->progress_eeprom, str_buf);
}
bool avr_isp_reader_view_input(InputEvent* event, void* context) {
furi_assert(context);
AvrIspReaderView* instance = context;
bool ret = true;
if(event->key == InputKeyBack && event->type == InputTypeShort) {
with_view_model(
instance->view,
AvrIspReaderViewModel * model,
{
if(model->status == AvrIspReaderViewStatusIDLE) {
if(instance->callback)
instance->callback(AvrIspCustomEventSceneExit, instance->context);
ret = false;
}
},
false);
} else if(event->key == InputKeyOk && event->type == InputTypeShort) {
with_view_model(
instance->view,
AvrIspReaderViewModel * model,
{
if(model->status == AvrIspReaderViewStatusIDLE) {
model->status = AvrIspReaderViewStatusReading;
avr_isp_worker_rw_read_dump_start(
instance->avr_isp_worker_rw, instance->file_path, instance->file_name);
}
},
false);
}
return ret;
}
static void avr_isp_reader_callback_status(void* context, AvrIspWorkerRWStatus status) {
furi_assert(context);
AvrIspReaderView* instance = context;
with_view_model(
instance->view,
AvrIspReaderViewModel * model,
{
switch(status) {
case AvrIspWorkerRWStatusEndReading:
model->status = AvrIspReaderViewStatusVerification;
avr_isp_worker_rw_verification_start(
instance->avr_isp_worker_rw, instance->file_path, instance->file_name);
model->status = AvrIspReaderViewStatusVerification;
break;
case AvrIspWorkerRWStatusEndVerification:
if(instance->callback)
instance->callback(AvrIspCustomEventSceneReadingOk, instance->context);
break;
case AvrIspWorkerRWStatusErrorVerification:
if(instance->callback)
instance->callback(AvrIspCustomEventSceneErrorVerification, instance->context);
break;
default:
//AvrIspWorkerRWStatusErrorReading;
if(instance->callback)
instance->callback(AvrIspCustomEventSceneErrorReading, instance->context);
break;
}
},
true);
}
void avr_isp_reader_view_enter(void* context) {
furi_assert(context);
AvrIspReaderView* instance = context;
with_view_model(
instance->view,
AvrIspReaderViewModel * model,
{
model->status = AvrIspReaderViewStatusIDLE;
model->progress_flash = 0.0f;
model->progress_eeprom = 0.0f;
},
true);
//Start avr_isp_worker_rw
instance->avr_isp_worker_rw = avr_isp_worker_rw_alloc(instance->context);
avr_isp_worker_rw_set_callback_status(
instance->avr_isp_worker_rw, avr_isp_reader_callback_status, instance);
avr_isp_worker_rw_start(instance->avr_isp_worker_rw);
}
void avr_isp_reader_view_exit(void* context) {
furi_assert(context);
AvrIspReaderView* instance = context;
//Stop avr_isp_worker_rw
if(avr_isp_worker_rw_is_running(instance->avr_isp_worker_rw)) {
avr_isp_worker_rw_stop(instance->avr_isp_worker_rw);
}
avr_isp_worker_rw_free(instance->avr_isp_worker_rw);
}
AvrIspReaderView* avr_isp_reader_view_alloc() {
AvrIspReaderView* instance = malloc(sizeof(AvrIspReaderView));
// View allocation and configuration
instance->view = view_alloc();
view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspReaderViewModel));
view_set_context(instance->view, instance);
view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_reader_view_draw);
view_set_input_callback(instance->view, avr_isp_reader_view_input);
view_set_enter_callback(instance->view, avr_isp_reader_view_enter);
view_set_exit_callback(instance->view, avr_isp_reader_view_exit);
return instance;
}
void avr_isp_reader_view_free(AvrIspReaderView* instance) {
furi_assert(instance);
view_free(instance->view);
free(instance);
}
View* avr_isp_reader_view_get_view(AvrIspReaderView* instance) {
furi_assert(instance);
return instance->view;
}

View file

@ -1,35 +0,0 @@
#pragma once
#include <gui/view.h>
#include "../helpers/avr_isp_types.h"
#include "../helpers/avr_isp_event.h"
typedef struct AvrIspReaderView AvrIspReaderView;
typedef void (*AvrIspReaderViewCallback)(AvrIspCustomEvent event, void* context);
typedef enum {
AvrIspReaderViewStatusIDLE,
AvrIspReaderViewStatusReading,
AvrIspReaderViewStatusVerification,
} AvrIspReaderViewStatus;
void avr_isp_reader_update_progress(AvrIspReaderView* instance);
void avr_isp_reader_set_file_path(
AvrIspReaderView* instance,
const char* file_path,
const char* file_name);
void avr_isp_reader_view_set_callback(
AvrIspReaderView* instance,
AvrIspReaderViewCallback callback,
void* context);
AvrIspReaderView* avr_isp_reader_view_alloc();
void avr_isp_reader_view_free(AvrIspReaderView* instance);
View* avr_isp_reader_view_get_view(AvrIspReaderView* instance);
void avr_isp_reader_view_exit(void* context);

View file

@ -1,268 +0,0 @@
#include "avr_isp_view_writer.h"
#include <gui/elements.h>
#include "../helpers/avr_isp_worker_rw.h"
#include <float_tools.h>
struct AvrIspWriterView {
View* view;
AvrIspWorkerRW* avr_isp_worker_rw;
const char* file_path;
const char* file_name;
AvrIspWriterViewCallback callback;
void* context;
};
typedef struct {
AvrIspWriterViewStatus status;
float progress_flash;
float progress_eeprom;
} AvrIspWriterViewModel;
void avr_isp_writer_update_progress(AvrIspWriterView* instance) {
with_view_model(
instance->view,
AvrIspWriterViewModel * model,
{
model->progress_flash =
avr_isp_worker_rw_get_progress_flash(instance->avr_isp_worker_rw);
model->progress_eeprom =
avr_isp_worker_rw_get_progress_eeprom(instance->avr_isp_worker_rw);
},
true);
}
void avr_isp_writer_view_set_callback(
AvrIspWriterView* instance,
AvrIspWriterViewCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
void avr_isp_writer_set_file_path(
AvrIspWriterView* instance,
const char* file_path,
const char* file_name) {
furi_assert(instance);
instance->file_path = file_path;
instance->file_name = file_name;
}
void avr_isp_writer_view_draw(Canvas* canvas, AvrIspWriterViewModel* model) {
canvas_clear(canvas);
char str_flash[32] = {0};
char str_eeprom[32] = {0};
canvas_set_font(canvas, FontPrimary);
switch(model->status) {
case AvrIspWriterViewStatusIDLE:
canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Press start to write");
canvas_set_font(canvas, FontSecondary);
elements_button_center(canvas, "Start");
snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100));
snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100));
break;
case AvrIspWriterViewStatusWriting:
if(float_is_equal(model->progress_flash, 0.f)) {
canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Verifying firmware");
snprintf(str_flash, sizeof(str_flash), "***");
snprintf(str_eeprom, sizeof(str_eeprom), "***");
} else {
canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Writing dump");
snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100));
snprintf(
str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100));
}
break;
case AvrIspWriterViewStatusVerification:
canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Verifying dump");
snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100));
snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100));
break;
case AvrIspWriterViewStatusWritingFuse:
canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Writing fuse");
snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100));
snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100));
break;
case AvrIspWriterViewStatusWritingFuseOk:
canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Done!");
snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100));
snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100));
canvas_set_font(canvas, FontSecondary);
elements_button_center(canvas, "Reflash");
elements_button_right(canvas, "Exit");
break;
default:
break;
}
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 0, 27, "Flash");
// snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_flash * 100));
elements_progress_bar_with_text(canvas, 44, 17, 84, model->progress_flash, str_flash);
canvas_draw_str(canvas, 0, 43, "EEPROM");
// snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_eeprom * 100));
elements_progress_bar_with_text(canvas, 44, 34, 84, model->progress_eeprom, str_eeprom);
}
bool avr_isp_writer_view_input(InputEvent* event, void* context) {
furi_assert(context);
AvrIspWriterView* instance = context;
bool ret = true;
if(event->key == InputKeyBack && event->type == InputTypeShort) {
with_view_model(
instance->view,
AvrIspWriterViewModel * model,
{
if((model->status == AvrIspWriterViewStatusIDLE) ||
(model->status == AvrIspWriterViewStatusWritingFuseOk)) {
if(instance->callback)
instance->callback(AvrIspCustomEventSceneExit, instance->context);
ret = false;
}
},
false);
} else if(event->key == InputKeyOk && event->type == InputTypeShort) {
with_view_model(
instance->view,
AvrIspWriterViewModel * model,
{
if((model->status == AvrIspWriterViewStatusIDLE) ||
(model->status == AvrIspWriterViewStatusWritingFuseOk)) {
model->status = AvrIspWriterViewStatusWriting;
avr_isp_worker_rw_write_dump_start(
instance->avr_isp_worker_rw, instance->file_path, instance->file_name);
}
},
false);
} else if(event->key == InputKeyRight && event->type == InputTypeShort) {
with_view_model(
instance->view,
AvrIspWriterViewModel * model,
{
if((model->status == AvrIspWriterViewStatusIDLE) ||
(model->status == AvrIspWriterViewStatusWritingFuseOk)) {
if(instance->callback)
instance->callback(AvrIspCustomEventSceneExitStartMenu, instance->context);
ret = false;
}
},
false);
}
return ret;
}
static void avr_isp_writer_callback_status(void* context, AvrIspWorkerRWStatus status) {
furi_assert(context);
AvrIspWriterView* instance = context;
with_view_model(
instance->view,
AvrIspWriterViewModel * model,
{
switch(status) {
case AvrIspWorkerRWStatusEndWriting:
model->status = AvrIspWriterViewStatusVerification;
avr_isp_worker_rw_verification_start(
instance->avr_isp_worker_rw, instance->file_path, instance->file_name);
model->status = AvrIspWriterViewStatusVerification;
break;
case AvrIspWorkerRWStatusErrorVerification:
if(instance->callback)
instance->callback(AvrIspCustomEventSceneErrorVerification, instance->context);
break;
case AvrIspWorkerRWStatusEndVerification:
avr_isp_worker_rw_write_fuse_start(
instance->avr_isp_worker_rw, instance->file_path, instance->file_name);
model->status = AvrIspWriterViewStatusWritingFuse;
break;
case AvrIspWorkerRWStatusErrorWritingFuse:
if(instance->callback)
instance->callback(AvrIspCustomEventSceneErrorWritingFuse, instance->context);
break;
case AvrIspWorkerRWStatusEndWritingFuse:
model->status = AvrIspWriterViewStatusWritingFuseOk;
break;
default:
//AvrIspWorkerRWStatusErrorWriting;
if(instance->callback)
instance->callback(AvrIspCustomEventSceneErrorWriting, instance->context);
break;
}
},
true);
}
void avr_isp_writer_view_enter(void* context) {
furi_assert(context);
AvrIspWriterView* instance = context;
with_view_model(
instance->view,
AvrIspWriterViewModel * model,
{
model->status = AvrIspWriterViewStatusIDLE;
model->progress_flash = 0.0f;
model->progress_eeprom = 0.0f;
},
true);
//Start avr_isp_worker_rw
instance->avr_isp_worker_rw = avr_isp_worker_rw_alloc(instance->context);
avr_isp_worker_rw_set_callback_status(
instance->avr_isp_worker_rw, avr_isp_writer_callback_status, instance);
avr_isp_worker_rw_start(instance->avr_isp_worker_rw);
}
void avr_isp_writer_view_exit(void* context) {
furi_assert(context);
AvrIspWriterView* instance = context;
//Stop avr_isp_worker_rw
if(avr_isp_worker_rw_is_running(instance->avr_isp_worker_rw)) {
avr_isp_worker_rw_stop(instance->avr_isp_worker_rw);
}
avr_isp_worker_rw_free(instance->avr_isp_worker_rw);
}
AvrIspWriterView* avr_isp_writer_view_alloc() {
AvrIspWriterView* instance = malloc(sizeof(AvrIspWriterView));
// View allocation and configuration
instance->view = view_alloc();
view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspWriterViewModel));
view_set_context(instance->view, instance);
view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_writer_view_draw);
view_set_input_callback(instance->view, avr_isp_writer_view_input);
view_set_enter_callback(instance->view, avr_isp_writer_view_enter);
view_set_exit_callback(instance->view, avr_isp_writer_view_exit);
return instance;
}
void avr_isp_writer_view_free(AvrIspWriterView* instance) {
furi_assert(instance);
view_free(instance->view);
free(instance);
}
View* avr_isp_writer_view_get_view(AvrIspWriterView* instance) {
furi_assert(instance);
return instance->view;
}

View file

@ -1,37 +0,0 @@
#pragma once
#include <gui/view.h>
#include "../helpers/avr_isp_types.h"
#include "../helpers/avr_isp_event.h"
typedef struct AvrIspWriterView AvrIspWriterView;
typedef void (*AvrIspWriterViewCallback)(AvrIspCustomEvent event, void* context);
typedef enum {
AvrIspWriterViewStatusIDLE,
AvrIspWriterViewStatusWriting,
AvrIspWriterViewStatusVerification,
AvrIspWriterViewStatusWritingFuse,
AvrIspWriterViewStatusWritingFuseOk,
} AvrIspWriterViewStatus;
void avr_isp_writer_update_progress(AvrIspWriterView* instance);
void avr_isp_writer_set_file_path(
AvrIspWriterView* instance,
const char* file_path,
const char* file_name);
void avr_isp_writer_view_set_callback(
AvrIspWriterView* instance,
AvrIspWriterViewCallback callback,
void* context);
AvrIspWriterView* avr_isp_writer_view_alloc();
void avr_isp_writer_view_free(AvrIspWriterView* instance);
View* avr_isp_writer_view_get_view(AvrIspWriterView* instance);
void avr_isp_writer_view_exit(void* context);

View file

@ -1,16 +0,0 @@
App(
appid="bad_bt",
name="Bad BT",
apptype=FlipperAppType.EXTERNAL,
entry_point="bad_bt_app",
requires=[
"gui",
"dialogs",
],
stack_size=2 * 1024,
order=70,
fap_libs=["assets"],
fap_category="Bluetooth",
fap_icon="images/badbt_10px.png",
fap_icon_assets="images",
)

View file

@ -1,333 +0,0 @@
#include "bad_bt_app.h"
#include <furi.h>
#include <furi_hal.h>
#include <storage/storage.h>
#include <lib/toolbox/path.h>
#include <lib/flipper_format/flipper_format.h>
#include <bt/bt_service/bt_i.h>
#include <bt/bt_service/bt.h>
#define BAD_BT_SETTINGS_FILE_NAME ".badbt.settings"
#define BAD_BT_APP_PATH_BOUND_KEYS_FOLDER EXT_PATH("badbt")
#define BAD_BT_APP_PATH_BOUND_KEYS_FILE BAD_BT_APP_PATH_BOUND_KEYS_FOLDER "/.badbt.keys"
#define BAD_BT_SETTINGS_PATH BAD_BT_APP_BASE_CONFIG_FOLDER "/" BAD_BT_SETTINGS_FILE_NAME
static bool bad_bt_app_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
BadBtApp* app = context;
return scene_manager_handle_custom_event(app->scene_manager, event);
}
static bool bad_bt_app_back_event_callback(void* context) {
furi_assert(context);
BadBtApp* app = context;
return scene_manager_handle_back_event(app->scene_manager);
}
static void bad_bt_app_tick_event_callback(void* context) {
furi_assert(context);
BadBtApp* app = context;
scene_manager_handle_tick_event(app->scene_manager);
}
static void bad_bt_load_settings(BadBtApp* app) {
furi_string_reset(app->keyboard_layout);
strcpy(app->config.bt_name, "");
memcpy(
app->config.bt_mac,
furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard),
BAD_BT_MAC_ADDRESS_LEN);
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* file = flipper_format_file_alloc(storage);
if(flipper_format_file_open_existing(file, BAD_BT_SETTINGS_PATH)) {
FuriString* tmp_str = furi_string_alloc();
if(!flipper_format_read_string(file, "Keyboard_Layout", app->keyboard_layout)) {
furi_string_reset(app->keyboard_layout);
}
if(!flipper_format_read_bool(file, "BT_Remember", &(app->bt_remember), 1)) {
app->bt_remember = false;
}
if(flipper_format_read_string(file, "Bt_Name", tmp_str) && !furi_string_empty(tmp_str)) {
strcpy(app->config.bt_name, furi_string_get_cstr(tmp_str));
} else {
strcpy(app->config.bt_name, "");
}
if(!flipper_format_read_hex(
file, "Bt_Mac", (uint8_t*)&app->config.bt_mac, BAD_BT_MAC_ADDRESS_LEN)) {
memcpy(
app->config.bt_mac,
furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard),
BAD_BT_MAC_ADDRESS_LEN);
}
furi_string_free(tmp_str);
flipper_format_file_close(file);
}
flipper_format_free(file);
if(!furi_string_empty(app->keyboard_layout)) {
FileInfo layout_file_info;
FS_Error file_check_err = storage_common_stat(
storage, furi_string_get_cstr(app->keyboard_layout), &layout_file_info);
if(file_check_err != FSE_OK) {
furi_string_reset(app->keyboard_layout);
return;
}
if(layout_file_info.size != 256) {
furi_string_reset(app->keyboard_layout);
}
}
furi_record_close(RECORD_STORAGE);
}
static void bad_bt_save_settings(BadBtApp* app) {
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* file = flipper_format_file_alloc(storage);
if(flipper_format_file_open_always(file, BAD_BT_SETTINGS_PATH)) {
flipper_format_write_string(file, "Keyboard_Layout", app->keyboard_layout);
flipper_format_write_bool(file, "BT_Remember", &(app->bt_remember), 1);
flipper_format_write_string_cstr(file, "Bt_Name", app->config.bt_name);
flipper_format_write_hex(
file, "Bt_Mac", (uint8_t*)&app->config.bt_mac, BAD_BT_MAC_ADDRESS_LEN);
flipper_format_file_close(file);
}
flipper_format_free(file);
furi_record_close(RECORD_STORAGE);
}
void bad_bt_reload_worker(BadBtApp* app) {
bad_bt_script_close(app->bad_bt_script);
app->bad_bt_script = bad_bt_script_open(app->file_path, app->bt, app);
bad_bt_script_set_keyboard_layout(app->bad_bt_script, app->keyboard_layout);
}
void bad_kb_config_refresh_menu(BadBtApp* app) {
scene_manager_next_scene(app->scene_manager, BadBtSceneConfig);
scene_manager_previous_scene(app->scene_manager);
}
int32_t bad_bt_config_switch_mode(BadBtApp* app) {
bad_bt_reload_worker(app);
furi_hal_bt_start_advertising();
bad_kb_config_refresh_menu(app);
return 0;
}
void bad_bt_config_switch_remember_mode(BadBtApp* app) {
if(app->bt_remember) {
furi_hal_bt_set_profile_pairing_method(
FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo);
bt_set_profile_mac_address(app->bt, (uint8_t*)&BAD_BT_BOUND_MAC_ADDRESS);
bt_enable_peer_key_update(app->bt);
} else {
furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone);
bt_set_profile_mac_address(app->bt, app->config.bt_mac);
bt_disable_peer_key_update(app->bt);
}
bad_bt_reload_worker(app);
}
int32_t bad_bt_connection_init(BadBtApp* app) {
// Set original name and mac address in prev config
strcpy(
app->prev_config.bt_name, furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard));
memcpy(app->prev_config.bt_mac, furi_hal_version_get_ble_mac(), BAD_BT_MAC_ADDRESS_LEN);
bt_timeout = bt_hid_delays[LevelRssi39_0];
bt_disconnect(app->bt);
// Wait 2nd core to update nvm storage
furi_delay_ms(200);
bt_keys_storage_set_storage_path(app->bt, BAD_BT_APP_PATH_BOUND_KEYS_FILE);
if(strcmp(app->config.bt_name, "") != 0) {
furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->config.bt_name);
}
if(app->bt_remember) {
furi_hal_bt_set_profile_mac_addr(
FuriHalBtProfileHidKeyboard, (uint8_t*)&BAD_BT_BOUND_MAC_ADDRESS);
furi_hal_bt_set_profile_pairing_method(
FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo);
} else {
if(memcmp(
app->config.bt_mac, (uint8_t*)&BAD_BT_EMPTY_MAC_ADDRESS, BAD_BT_MAC_ADDRESS_LEN) !=
0) {
furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->config.bt_mac);
}
furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone);
}
bt_set_profile(app->bt, BtProfileHidKeyboard);
if(strcmp(app->config.bt_name, "") == 0) {
strcpy(app->config.bt_name, furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard));
}
if(memcmp(app->config.bt_mac, (uint8_t*)&BAD_BT_EMPTY_MAC_ADDRESS, BAD_BT_MAC_ADDRESS_LEN) ==
0) {
memcpy(
app->config.bt_mac,
furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard),
BAD_BT_MAC_ADDRESS_LEN);
}
furi_hal_bt_start_advertising();
if(app->bt_remember) {
bt_enable_peer_key_update(app->bt);
} else {
bt_disable_peer_key_update(app->bt);
}
return 0;
}
void bad_bt_connection_deinit(BadBtApp* app) {
bt_disconnect(app->bt);
// Wait 2nd core to update nvm storage
furi_delay_ms(200);
bt_keys_storage_set_default_path(app->bt);
furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->prev_config.bt_name);
furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->prev_config.bt_mac);
furi_hal_bt_set_profile_pairing_method(
FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo);
bt_set_profile(app->bt, BtProfileSerial);
bt_enable_peer_key_update(app->bt);
}
BadBtApp* bad_bt_app_alloc(char* arg) {
BadBtApp* app = malloc(sizeof(BadBtApp));
app->bad_bt_script = NULL;
app->file_path = furi_string_alloc();
app->keyboard_layout = furi_string_alloc();
if(arg && strlen(arg)) {
furi_string_set(app->file_path, arg);
}
Storage* storage = furi_record_open(RECORD_STORAGE);
storage_simply_mkdir(storage, BAD_BT_APP_BASE_CONFIG_FOLDER);
furi_record_close(RECORD_STORAGE);
bad_bt_load_settings(app);
app->gui = furi_record_open(RECORD_GUI);
app->notifications = furi_record_open(RECORD_NOTIFICATION);
app->dialogs = furi_record_open(RECORD_DIALOGS);
app->view_dispatcher = view_dispatcher_alloc();
view_dispatcher_enable_queue(app->view_dispatcher);
app->scene_manager = scene_manager_alloc(&bad_bt_scene_handlers, app);
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
view_dispatcher_set_tick_event_callback(
app->view_dispatcher, bad_bt_app_tick_event_callback, 500);
view_dispatcher_set_custom_event_callback(
app->view_dispatcher, bad_bt_app_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, bad_bt_app_back_event_callback);
Bt* bt = furi_record_open(RECORD_BT);
app->bt = bt;
app->bt->suppress_pin_screen = true;
// Custom Widget
app->widget = widget_alloc();
view_dispatcher_add_view(
app->view_dispatcher, BadBtAppViewError, widget_get_view(app->widget));
app->var_item_list = variable_item_list_alloc();
view_dispatcher_add_view(
app->view_dispatcher, BadBtAppViewConfig, variable_item_list_get_view(app->var_item_list));
app->bad_bt_view = bad_bt_alloc();
view_dispatcher_add_view(
app->view_dispatcher, BadBtAppViewWork, bad_bt_get_view(app->bad_bt_view));
app->text_input = text_input_alloc();
view_dispatcher_add_view(
app->view_dispatcher, BadBtAppViewConfigName, text_input_get_view(app->text_input));
app->byte_input = byte_input_alloc();
view_dispatcher_add_view(
app->view_dispatcher, BadBtAppViewConfigMac, byte_input_get_view(app->byte_input));
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
app->conn_init_thread = furi_thread_alloc_ex(
"BadBtConnInit", 1024, (FuriThreadCallback)bad_bt_connection_init, app);
furi_thread_start(app->conn_init_thread);
if(!furi_string_empty(app->file_path)) {
app->bad_bt_script = bad_bt_script_open(app->file_path, app->bt, app);
bad_bt_script_set_keyboard_layout(app->bad_bt_script, app->keyboard_layout);
scene_manager_next_scene(app->scene_manager, BadBtSceneWork);
} else {
furi_string_set(app->file_path, BAD_BT_APP_BASE_FOLDER);
scene_manager_next_scene(app->scene_manager, BadBtSceneFileSelect);
}
return app;
}
void bad_bt_app_free(BadBtApp* app) {
furi_assert(app);
if(app->bad_bt_script) {
bad_bt_script_close(app->bad_bt_script);
app->bad_bt_script = NULL;
}
// Views
view_dispatcher_remove_view(app->view_dispatcher, BadBtAppViewWork);
bad_bt_free(app->bad_bt_view);
// Custom Widget
view_dispatcher_remove_view(app->view_dispatcher, BadBtAppViewError);
widget_free(app->widget);
// Variable item list
view_dispatcher_remove_view(app->view_dispatcher, BadBtAppViewConfig);
variable_item_list_free(app->var_item_list);
// Text Input
view_dispatcher_remove_view(app->view_dispatcher, BadBtAppViewConfigName);
text_input_free(app->text_input);
// Byte Input
view_dispatcher_remove_view(app->view_dispatcher, BadBtAppViewConfigMac);
byte_input_free(app->byte_input);
// View dispatcher
view_dispatcher_free(app->view_dispatcher);
scene_manager_free(app->scene_manager);
// Restore bt config
app->bt->suppress_pin_screen = false;
if(app->conn_init_thread) {
furi_thread_join(app->conn_init_thread);
furi_thread_free(app->conn_init_thread);
bad_bt_connection_deinit(app);
}
// Close records
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_NOTIFICATION);
furi_record_close(RECORD_DIALOGS);
furi_record_close(RECORD_BT);
bad_bt_save_settings(app);
furi_string_free(app->file_path);
furi_string_free(app->keyboard_layout);
free(app);
}
int32_t bad_bt_app(void* p) {
BadBtApp* bad_bt_app = bad_bt_app_alloc((char*)p);
view_dispatcher_run(bad_bt_app->view_dispatcher);
bad_bt_app_free(bad_bt_app);
return 0;
}

View file

@ -1,39 +0,0 @@
#pragma once
#include "scenes/bad_bt_scene.h"
#include "helpers/ducky_script.h"
#include <gui/gui.h>
#include <assets_icons.h>
#include <gui/scene_manager.h>
#include <dialogs/dialogs.h>
#include <notification/notification_messages.h>
#include "bad_bt_icons.h"
#define BAD_BT_APP_BASE_FOLDER EXT_PATH("badusb")
#define BAD_BT_APP_BASE_CONFIG_FOLDER EXT_PATH("badbt")
#define BAD_BT_APP_PATH_LAYOUT_FOLDER BAD_BT_APP_BASE_FOLDER "/assets/layouts"
#define BAD_BT_APP_SCRIPT_EXTENSION ".txt"
#define BAD_BT_APP_LAYOUT_EXTENSION ".kl"
typedef enum BadBtCustomEvent {
BadBtAppCustomEventTextEditResult,
BadBtAppCustomEventByteInputDone,
BadBtCustomEventErrorBack
} BadBtCustomEvent;
typedef enum {
BadBtAppViewError,
BadBtAppViewWork,
BadBtAppViewConfig,
BadBtAppViewConfigMac,
BadBtAppViewConfigName
} BadBtAppView;
void bad_bt_config_switch_remember_mode(BadBtApp* app);
int32_t bad_bt_connection_init(BadBtApp* app);
void bad_bt_connection_deinit(BadBtApp* app);
void bad_kb_config_refresh_menu(BadBtApp* app);

View file

@ -1,792 +0,0 @@
#include <furi.h>
#include <furi_hal.h>
#include <gui/gui.h>
#include <input/input.h>
#include <lib/toolbox/args.h>
#include <furi_hal_bt_hid.h>
#include <bt/bt_service/bt.h>
#include <storage/storage.h>
#include "ducky_script.h"
#include "ducky_script_i.h"
#include <dolphin/dolphin.h>
#include <toolbox/hex.h>
#include "../bad_bt_app.h"
const uint8_t BAD_BT_BOUND_MAC_ADDRESS[BAD_BT_MAC_ADDRESS_LEN] =
{0x41, 0x4a, 0xef, 0xb6, 0xa9, 0xd4};
const uint8_t BAD_BT_EMPTY_MAC_ADDRESS[BAD_BT_MAC_ADDRESS_LEN] =
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
#define TAG "BadBT"
#define WORKER_TAG TAG "Worker"
#define BADBT_ASCII_TO_KEY(script, x) \
(((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE)
/**
* Delays for waiting between HID key press and key release
*/
const uint8_t bt_hid_delays[LevelRssiNum] = {
60, // LevelRssi122_100
55, // LevelRssi99_80
50, // LevelRssi79_60
47, // LevelRssi59_40
34, // LevelRssi39_0
};
uint8_t bt_timeout = 0;
static LevelRssiRange bt_remote_rssi_range(Bt* bt) {
uint8_t rssi;
if(!bt_remote_rssi(bt, &rssi)) return LevelRssiError;
if(rssi <= 39)
return LevelRssi39_0;
else if(rssi <= 59)
return LevelRssi59_40;
else if(rssi <= 79)
return LevelRssi79_60;
else if(rssi <= 99)
return LevelRssi99_80;
else if(rssi <= 122)
return LevelRssi122_100;
return LevelRssiError;
}
static inline void update_bt_timeout(Bt* bt) {
LevelRssiRange r = bt_remote_rssi_range(bt);
if(r < LevelRssiNum) {
bt_timeout = bt_hid_delays[r];
FURI_LOG_D(WORKER_TAG, "BLE Key timeout : %u", bt_timeout);
}
}
typedef enum {
WorkerEvtStartStop = (1 << 0),
WorkerEvtPauseResume = (1 << 1),
WorkerEvtEnd = (1 << 2),
WorkerEvtConnect = (1 << 3),
WorkerEvtDisconnect = (1 << 4),
} WorkerEvtFlags;
static const char ducky_cmd_id[] = {"ID"};
static const char ducky_cmd_bt_id[] = {"BT_ID"};
static const uint8_t numpad_keys[10] = {
HID_KEYPAD_0,
HID_KEYPAD_1,
HID_KEYPAD_2,
HID_KEYPAD_3,
HID_KEYPAD_4,
HID_KEYPAD_5,
HID_KEYPAD_6,
HID_KEYPAD_7,
HID_KEYPAD_8,
HID_KEYPAD_9,
};
uint32_t ducky_get_command_len(const char* line) {
uint32_t len = strlen(line);
for(uint32_t i = 0; i < len; i++) {
if(line[i] == ' ') return i;
}
return 0;
}
bool ducky_is_line_end(const char chr) {
return ((chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n'));
}
uint16_t ducky_get_keycode(BadBtScript* bad_bt, const char* param, bool accept_chars) {
uint16_t keycode = ducky_get_keycode_by_name(param);
if(keycode != HID_KEYBOARD_NONE) {
return keycode;
}
if((accept_chars) && (strlen(param) > 0)) {
return (BADBT_ASCII_TO_KEY(bad_bt, param[0]) & 0xFF);
}
return 0;
}
bool ducky_get_number(const char* param, uint32_t* val) {
uint32_t value = 0;
if(sscanf(param, "%lu", &value) == 1) {
*val = value;
return true;
}
return false;
}
void ducky_numlock_on(BadBtScript* bad_bt) {
UNUSED(bad_bt);
if((furi_hal_bt_hid_get_led_state() & HID_KB_LED_NUM) == 0) {
furi_hal_bt_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK);
furi_delay_ms(bt_timeout);
furi_hal_bt_hid_kb_release(HID_KEYBOARD_LOCK_NUM_LOCK);
}
}
bool ducky_numpad_press(BadBtScript* bad_bt, const char num) {
UNUSED(bad_bt);
if((num < '0') || (num > '9')) return false;
uint16_t key = numpad_keys[num - '0'];
furi_hal_bt_hid_kb_press(key);
furi_delay_ms(bt_timeout);
furi_hal_bt_hid_kb_release(key);
return true;
}
bool ducky_altchar(BadBtScript* bad_bt, const char* charcode) {
uint8_t i = 0;
bool state = false;
furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT);
while(!ducky_is_line_end(charcode[i])) {
state = ducky_numpad_press(bad_bt, charcode[i]);
if(state == false) break;
i++;
}
furi_hal_bt_hid_kb_release(KEY_MOD_LEFT_ALT);
return state;
}
bool ducky_altstring(BadBtScript* bad_bt, const char* param) {
uint32_t i = 0;
bool state = false;
while(param[i] != '\0') {
if((param[i] < ' ') || (param[i] > '~')) {
i++;
continue; // Skip non-printable chars
}
char temp_str[4];
snprintf(temp_str, 4, "%u", param[i]);
state = ducky_altchar(bad_bt, temp_str);
if(state == false) break;
i++;
}
return state;
}
int32_t ducky_error(BadBtScript* bad_bt, const char* text, ...) {
va_list args;
va_start(args, text);
vsnprintf(bad_bt->st.error, sizeof(bad_bt->st.error), text, args);
va_end(args);
return SCRIPT_STATE_ERROR;
}
bool ducky_string(BadBtScript* bad_bt, const char* param) {
uint32_t i = 0;
while(param[i] != '\0') {
if(param[i] != '\n') {
uint16_t keycode = BADBT_ASCII_TO_KEY(bad_bt, param[i]);
if(keycode != HID_KEYBOARD_NONE) {
furi_hal_bt_hid_kb_press(keycode);
furi_delay_ms(bt_timeout);
furi_hal_bt_hid_kb_release(keycode);
}
} else {
furi_hal_bt_hid_kb_press(HID_KEYBOARD_RETURN);
furi_delay_ms(bt_timeout);
furi_hal_bt_hid_kb_release(HID_KEYBOARD_RETURN);
}
i++;
}
bad_bt->stringdelay = 0;
return true;
}
static bool ducky_string_next(BadBtScript* bad_bt) {
if(bad_bt->string_print_pos >= furi_string_size(bad_bt->string_print)) {
return true;
}
char print_char = furi_string_get_char(bad_bt->string_print, bad_bt->string_print_pos);
if(print_char != '\n') {
uint16_t keycode = BADBT_ASCII_TO_KEY(bad_bt, print_char);
if(keycode != HID_KEYBOARD_NONE) {
furi_hal_bt_hid_kb_press(keycode);
furi_delay_ms(bt_timeout);
furi_hal_bt_hid_kb_release(keycode);
}
} else {
furi_hal_bt_hid_kb_press(HID_KEYBOARD_RETURN);
furi_delay_ms(bt_timeout);
furi_hal_bt_hid_kb_release(HID_KEYBOARD_RETURN);
}
bad_bt->string_print_pos++;
return false;
}
static int32_t ducky_parse_line(BadBtScript* bad_bt, FuriString* line) {
uint32_t line_len = furi_string_size(line);
const char* line_tmp = furi_string_get_cstr(line);
if(line_len == 0) {
return SCRIPT_STATE_NEXT_LINE; // Skip empty lines
}
FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp);
// Ducky Lang Functions
int32_t cmd_result = ducky_execute_cmd(bad_bt, line_tmp);
if(cmd_result != SCRIPT_STATE_CMD_UNKNOWN) {
return cmd_result;
}
// Special keys + modifiers
uint16_t key = ducky_get_keycode(bad_bt, line_tmp, false);
if(key == HID_KEYBOARD_NONE) {
return ducky_error(bad_bt, "No keycode defined for %s", line_tmp);
}
if((key & 0xFF00) != 0) {
// It's a modifier key
uint32_t offset = ducky_get_command_len(line_tmp) + 1;
// ducky_get_command_len() returns 0 without space, so check for != 1
if(offset != 1 && line_len > offset) {
// It's also a key combination
key |= ducky_get_keycode(bad_bt, line_tmp + offset, true);
}
}
furi_hal_bt_hid_kb_press(key);
furi_delay_ms(bt_timeout);
furi_hal_bt_hid_kb_release(key);
return 0;
}
static bool ducky_set_bt_id(BadBtScript* bad_bt, const char* line) {
size_t line_len = strlen(line);
size_t mac_len = BAD_BT_MAC_ADDRESS_LEN * 3;
if(line_len < mac_len + 1) return false; // MAC + at least 1 char for name
uint8_t mac[BAD_BT_MAC_ADDRESS_LEN];
for(size_t i = 0; i < BAD_BT_MAC_ADDRESS_LEN; i++) {
char a = line[i * 3];
char b = line[i * 3 + 1];
if((a < 'A' && a > 'F') || (a < '0' && a > '9') || (b < 'A' && b > 'F') ||
(b < '0' && b > '9') || !hex_char_to_uint8(a, b, &mac[i])) {
return false;
}
}
furi_hal_bt_reverse_mac_addr(mac);
furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, line + mac_len);
bt_set_profile_mac_address(bad_bt->bt, mac);
return true;
}
static bool ducky_script_preload(BadBtScript* bad_bt, File* script_file) {
uint8_t ret = 0;
uint32_t line_len = 0;
furi_string_reset(bad_bt->line);
do {
ret = storage_file_read(script_file, bad_bt->file_buf, FILE_BUFFER_LEN);
for(uint16_t i = 0; i < ret; i++) {
if(bad_bt->file_buf[i] == '\n' && line_len > 0) {
bad_bt->st.line_nb++;
line_len = 0;
} else {
if(bad_bt->st.line_nb == 0) { // Save first line
furi_string_push_back(bad_bt->line, bad_bt->file_buf[i]);
}
line_len++;
}
}
if(storage_file_eof(script_file)) {
if(line_len > 0) {
bad_bt->st.line_nb++;
break;
}
}
} while(ret > 0);
const char* line_tmp = furi_string_get_cstr(bad_bt->line);
if(bad_bt->app->switch_mode_thread) {
furi_thread_join(bad_bt->app->switch_mode_thread);
furi_thread_free(bad_bt->app->switch_mode_thread);
bad_bt->app->switch_mode_thread = NULL;
}
// Looking for ID or BT_ID command at first line
bad_bt->set_usb_id = false;
bad_bt->set_bt_id = false;
bad_bt->has_usb_id = strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0;
// TODO: We setting has_usb_id to its value but ignoring it for now and not using anywhere here, may be used in a future to detect script type
bad_bt->has_bt_id = strncmp(line_tmp, ducky_cmd_bt_id, strlen(ducky_cmd_bt_id)) == 0;
if(bad_bt->has_bt_id) {
if(!bad_bt->app->bt_remember) {
bad_bt->set_bt_id = ducky_set_bt_id(bad_bt, &line_tmp[strlen(ducky_cmd_bt_id) + 1]);
}
}
bad_kb_config_refresh_menu(bad_bt->app);
if(!bad_bt->set_bt_id) {
const char* bt_name = bad_bt->app->config.bt_name;
const uint8_t* bt_mac = bad_bt->app->bt_remember ? (uint8_t*)&BAD_BT_BOUND_MAC_ADDRESS :
bad_bt->app->config.bt_mac;
bool reset_name = strncmp(
bt_name,
furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard),
BAD_BT_ADV_NAME_MAX_LEN);
bool reset_mac = memcmp(
bt_mac,
furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard),
BAD_BT_MAC_ADDRESS_LEN);
if(reset_name && reset_mac) {
furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, bt_name);
} else if(reset_name) {
bt_set_profile_adv_name(bad_bt->bt, bt_name);
}
if(reset_mac) {
bt_set_profile_mac_address(bad_bt->bt, bt_mac);
}
}
storage_file_seek(script_file, 0, true);
furi_string_reset(bad_bt->line);
return true;
}
static int32_t ducky_script_execute_next(BadBtScript* bad_bt, File* script_file) {
int32_t delay_val = 0;
if(bad_bt->repeat_cnt > 0) {
bad_bt->repeat_cnt--;
delay_val = ducky_parse_line(bad_bt, bad_bt->line_prev);
if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
return 0;
} else if(delay_val == SCRIPT_STATE_STRING_START) { // Print string with delays
return delay_val;
} else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // wait for button
return delay_val;
} else if(delay_val < 0) { // Script error
bad_bt->st.error_line = bad_bt->st.line_cur - 1;
FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_bt->st.line_cur - 1U);
return SCRIPT_STATE_ERROR;
} else {
return (delay_val + bad_bt->defdelay);
}
}
furi_string_set(bad_bt->line_prev, bad_bt->line);
furi_string_reset(bad_bt->line);
while(1) {
if(bad_bt->buf_len == 0) {
bad_bt->buf_len = storage_file_read(script_file, bad_bt->file_buf, FILE_BUFFER_LEN);
if(storage_file_eof(script_file)) {
if((bad_bt->buf_len < FILE_BUFFER_LEN) && (bad_bt->file_end == false)) {
bad_bt->file_buf[bad_bt->buf_len] = '\n';
bad_bt->buf_len++;
bad_bt->file_end = true;
}
}
bad_bt->buf_start = 0;
if(bad_bt->buf_len == 0) return SCRIPT_STATE_END;
}
for(uint8_t i = bad_bt->buf_start; i < (bad_bt->buf_start + bad_bt->buf_len); i++) {
if(bad_bt->file_buf[i] == '\n' && furi_string_size(bad_bt->line) > 0) {
bad_bt->st.line_cur++;
bad_bt->buf_len = bad_bt->buf_len + bad_bt->buf_start - (i + 1);
bad_bt->buf_start = i + 1;
furi_string_trim(bad_bt->line);
delay_val = ducky_parse_line(bad_bt, bad_bt->line);
if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
return 0;
} else if(delay_val == SCRIPT_STATE_STRING_START) { // Print string with delays
return delay_val;
} else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // wait for button
return delay_val;
} else if(delay_val < 0) {
bad_bt->st.error_line = bad_bt->st.line_cur;
FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_bt->st.line_cur);
return SCRIPT_STATE_ERROR;
} else {
return (delay_val + bad_bt->defdelay);
}
} else {
furi_string_push_back(bad_bt->line, bad_bt->file_buf[i]);
}
}
bad_bt->buf_len = 0;
if(bad_bt->file_end) return SCRIPT_STATE_END;
}
return 0;
}
static void bad_bt_bt_hid_state_callback(BtStatus status, void* context) {
furi_assert(context);
BadBtScript* bad_bt = context;
bool state = (status == BtStatusConnected);
if(state == true) {
LevelRssiRange r = bt_remote_rssi_range(bad_bt->bt);
if(r != LevelRssiError) {
bt_timeout = bt_hid_delays[r];
}
furi_thread_flags_set(furi_thread_get_id(bad_bt->thread), WorkerEvtConnect);
} else {
furi_thread_flags_set(furi_thread_get_id(bad_bt->thread), WorkerEvtDisconnect);
}
}
static uint32_t bad_bt_flags_get(uint32_t flags_mask, uint32_t timeout) {
uint32_t flags = furi_thread_flags_get();
furi_check((flags & FuriFlagError) == 0);
if(flags == 0) {
flags = furi_thread_flags_wait(flags_mask, FuriFlagWaitAny, timeout);
furi_check(((flags & FuriFlagError) == 0) || (flags == (unsigned)FuriFlagErrorTimeout));
} else {
uint32_t state = furi_thread_flags_clear(flags);
furi_check((state & FuriFlagError) == 0);
}
return flags;
}
static int32_t bad_bt_worker(void* context) {
BadBtScript* bad_bt = context;
BadBtWorkerState worker_state = BadBtStateInit;
int32_t delay_val = 0;
FURI_LOG_I(WORKER_TAG, "Init");
File* script_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
bad_bt->line = furi_string_alloc();
bad_bt->line_prev = furi_string_alloc();
bad_bt->string_print = furi_string_alloc();
bt_set_status_changed_callback(bad_bt->bt, bad_bt_bt_hid_state_callback, bad_bt);
while(1) {
if(worker_state == BadBtStateInit) { // State: initialization
if(storage_file_open(
script_file,
furi_string_get_cstr(bad_bt->file_path),
FSAM_READ,
FSOM_OPEN_EXISTING)) {
if((ducky_script_preload(bad_bt, script_file)) && (bad_bt->st.line_nb > 0)) {
if(furi_hal_bt_is_connected()) {
worker_state = BadBtStateIdle; // Ready to run
} else {
worker_state = BadBtStateNotConnected; // Not connected
}
} else {
worker_state = BadBtStateScriptError; // Script preload error
}
} else {
FURI_LOG_E(WORKER_TAG, "File open error");
worker_state = BadBtStateFileError; // File open error
}
bad_bt->st.state = worker_state;
} else if(worker_state == BadBtStateNotConnected) { // State: Not connected
uint32_t flags = bad_bt_flags_get(
WorkerEvtEnd | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtStartStop,
FuriWaitForever);
if(flags & WorkerEvtEnd) {
break;
} else if(flags & WorkerEvtConnect) {
worker_state = BadBtStateIdle; // Ready to run
} else if(flags & WorkerEvtStartStop) {
worker_state = BadBtStateWillRun; // Will run when connected
}
bad_bt->st.state = worker_state;
} else if(worker_state == BadBtStateIdle) { // State: ready to start
uint32_t flags = bad_bt_flags_get(
WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtConnect | WorkerEvtDisconnect,
FuriWaitForever);
if(flags & WorkerEvtEnd) {
break;
} else if(flags & WorkerEvtStartStop) { // Start executing script
delay_val = 0;
bad_bt->buf_len = 0;
bad_bt->st.line_cur = 0;
bad_bt->defdelay = 0;
bad_bt->stringdelay = 0;
bad_bt->repeat_cnt = 0;
bad_bt->key_hold_nb = 0;
bad_bt->file_end = false;
storage_file_seek(script_file, 0, true);
bad_bt_script_set_keyboard_layout(bad_bt, bad_bt->keyboard_layout);
worker_state = BadBtStateRunning;
} else if(flags & WorkerEvtDisconnect) {
worker_state = BadBtStateNotConnected; // Disconnected
}
bad_bt->st.state = worker_state;
} else if(worker_state == BadBtStateWillRun) { // State: start on connection
uint32_t flags = bad_bt_flags_get(
WorkerEvtEnd | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtStartStop,
FuriWaitForever);
if(flags & WorkerEvtEnd) {
break;
} else if(flags & WorkerEvtConnect) { // Start executing script
delay_val = 0;
bad_bt->buf_len = 0;
bad_bt->st.line_cur = 0;
bad_bt->defdelay = 0;
bad_bt->stringdelay = 0;
bad_bt->repeat_cnt = 0;
bad_bt->file_end = false;
storage_file_seek(script_file, 0, true);
// extra time for PC to recognize Flipper as keyboard
flags = furi_thread_flags_wait(
WorkerEvtEnd | WorkerEvtDisconnect | WorkerEvtStartStop,
FuriFlagWaitAny | FuriFlagNoClear,
1500);
if(flags == (unsigned)FuriFlagErrorTimeout) {
// If nothing happened - start script execution
worker_state = BadBtStateRunning;
} else if(flags & WorkerEvtStartStop) {
worker_state = BadBtStateIdle;
furi_thread_flags_clear(WorkerEvtStartStop);
}
update_bt_timeout(bad_bt->bt);
bad_bt_script_set_keyboard_layout(bad_bt, bad_bt->keyboard_layout);
} else if(flags & WorkerEvtStartStop) { // Cancel scheduled execution
worker_state = BadBtStateNotConnected;
}
bad_bt->st.state = worker_state;
} else if(worker_state == BadBtStateRunning) { // State: running
uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val);
uint32_t flags = furi_thread_flags_wait(
WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtConnect | WorkerEvtDisconnect,
FuriFlagWaitAny,
delay_cur);
delay_val -= delay_cur;
if(!(flags & FuriFlagError)) {
if(flags & WorkerEvtEnd) {
break;
} else if(flags & WorkerEvtStartStop) {
worker_state = BadBtStateIdle; // Stop executing script
furi_hal_bt_hid_kb_release_all();
} else if(flags & WorkerEvtDisconnect) {
worker_state = BadBtStateNotConnected; // Disconnected
furi_hal_bt_hid_kb_release_all();
}
bad_bt->st.state = worker_state;
continue;
} else if(
(flags == (unsigned)FuriFlagErrorTimeout) ||
(flags == (unsigned)FuriFlagErrorResource)) {
if(delay_val > 0) {
bad_bt->st.delay_remain--;
continue;
}
bad_bt->st.state = BadBtStateRunning;
delay_val = ducky_script_execute_next(bad_bt, script_file);
if(delay_val == SCRIPT_STATE_ERROR) { // Script error
delay_val = 0;
worker_state = BadBtStateScriptError;
bad_bt->st.state = worker_state;
furi_hal_bt_hid_kb_release_all();
} else if(delay_val == SCRIPT_STATE_END) { // End of script
delay_val = 0;
worker_state = BadBtStateIdle;
bad_bt->st.state = BadBtStateDone;
furi_hal_bt_hid_kb_release_all();
continue;
} else if(delay_val == SCRIPT_STATE_STRING_START) { // Start printing string with delays
delay_val = bad_bt->defdelay;
bad_bt->string_print_pos = 0;
worker_state = BadBtStateStringDelay;
} else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // set state to wait for user input
worker_state = BadBtStateWaitForBtn;
bad_bt->st.state = BadBtStateWaitForBtn; // Show long delays
} else if(delay_val > 1000) {
bad_bt->st.state = BadBtStateDelay; // Show long delays
bad_bt->st.delay_remain = delay_val / 1000;
}
} else {
furi_check((flags & FuriFlagError) == 0);
}
} else if(worker_state == BadBtStateWaitForBtn) { // State: Wait for button Press
uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val);
uint32_t flags = furi_thread_flags_wait(
WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtConnect |
WorkerEvtDisconnect,
FuriFlagWaitAny,
delay_cur);
if(!(flags & FuriFlagError)) {
if(flags & WorkerEvtEnd) {
break;
} else if(flags & WorkerEvtStartStop) {
delay_val = 0;
worker_state = BadBtStateRunning;
} else if(flags & WorkerEvtDisconnect) {
worker_state = BadBtStateNotConnected; // Disconnected
furi_hal_hid_kb_release_all();
}
bad_bt->st.state = worker_state;
continue;
}
} else if(worker_state == BadBtStateStringDelay) { // State: print string with delays
uint32_t flags = furi_thread_flags_wait(
WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtConnect |
WorkerEvtDisconnect,
FuriFlagWaitAny,
bad_bt->stringdelay);
if(!(flags & FuriFlagError)) {
if(flags & WorkerEvtEnd) {
break;
} else if(flags & WorkerEvtStartStop) {
worker_state = BadBtStateIdle; // Stop executing script
furi_hal_bt_hid_kb_release_all();
} else if(flags & WorkerEvtDisconnect) {
worker_state = BadBtStateNotConnected; // Disconnected
furi_hal_bt_hid_kb_release_all();
}
bad_bt->st.state = worker_state;
continue;
} else if(
(flags == (unsigned)FuriFlagErrorTimeout) ||
(flags == (unsigned)FuriFlagErrorResource)) {
bool string_end = ducky_string_next(bad_bt);
if(string_end) {
bad_bt->stringdelay = 0;
worker_state = BadBtStateRunning;
}
} else {
furi_check((flags & FuriFlagError) == 0);
}
} else if(
(worker_state == BadBtStateFileError) ||
(worker_state == BadBtStateScriptError)) { // State: error
uint32_t flags =
bad_bt_flags_get(WorkerEvtEnd, FuriWaitForever); // Waiting for exit command
if(flags & WorkerEvtEnd) {
break;
}
}
update_bt_timeout(bad_bt->bt);
}
bt_set_status_changed_callback(bad_bt->bt, NULL, NULL);
storage_file_close(script_file);
storage_file_free(script_file);
furi_string_free(bad_bt->line);
furi_string_free(bad_bt->line_prev);
furi_string_free(bad_bt->string_print);
FURI_LOG_I(WORKER_TAG, "End");
return 0;
}
static void bad_bt_script_set_default_keyboard_layout(BadBtScript* bad_bt) {
furi_assert(bad_bt);
furi_string_set_str(bad_bt->keyboard_layout, "");
memset(bad_bt->layout, HID_KEYBOARD_NONE, sizeof(bad_bt->layout));
memcpy(bad_bt->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_bt->layout)));
}
BadBtScript* bad_bt_script_open(FuriString* file_path, Bt* bt, BadBtApp* app) {
furi_assert(file_path);
BadBtScript* bad_bt = malloc(sizeof(BadBtScript));
bad_bt->app = app;
bad_bt->file_path = furi_string_alloc();
furi_string_set(bad_bt->file_path, file_path);
bad_bt->keyboard_layout = furi_string_alloc();
bad_bt_script_set_default_keyboard_layout(bad_bt);
bad_bt->st.state = BadBtStateInit;
bad_bt->st.error[0] = '\0';
bad_bt->bt = bt;
bad_bt->thread = furi_thread_alloc_ex("BadBtWorker", 2048, bad_bt_worker, bad_bt);
furi_thread_start(bad_bt->thread);
return bad_bt;
}
void bad_bt_script_close(BadBtScript* bad_bt) {
furi_assert(bad_bt);
furi_record_close(RECORD_STORAGE);
furi_thread_flags_set(furi_thread_get_id(bad_bt->thread), WorkerEvtEnd);
furi_thread_join(bad_bt->thread);
furi_thread_free(bad_bt->thread);
furi_string_free(bad_bt->file_path);
furi_string_free(bad_bt->keyboard_layout);
free(bad_bt);
}
void bad_bt_script_set_keyboard_layout(BadBtScript* bad_bt, FuriString* layout_path) {
furi_assert(bad_bt);
if((bad_bt->st.state == BadBtStateRunning) || (bad_bt->st.state == BadBtStateDelay)) {
// do not update keyboard layout while a script is running
return;
}
File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
if(!furi_string_empty(layout_path)) { //-V1051
furi_string_set(bad_bt->keyboard_layout, layout_path);
if(storage_file_open(
layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) {
uint16_t layout[128];
if(storage_file_read(layout_file, layout, sizeof(layout)) == sizeof(layout)) {
memcpy(bad_bt->layout, layout, sizeof(layout));
}
}
storage_file_close(layout_file);
} else {
bad_bt_script_set_default_keyboard_layout(bad_bt);
}
storage_file_free(layout_file);
}
void bad_bt_script_toggle(BadBtScript* bad_bt) {
furi_assert(bad_bt);
furi_thread_flags_set(furi_thread_get_id(bad_bt->thread), WorkerEvtStartStop);
}
BadBtState* bad_bt_script_get_state(BadBtScript* bad_bt) {
furi_assert(bad_bt);
return &(bad_bt->st);
}

View file

@ -1,154 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <furi.h>
#include <furi_hal.h>
#include <bt/bt_service/bt_i.h>
#include <gui/view_dispatcher.h>
#include <gui/modules/widget.h>
#include <gui/modules/variable_item_list.h>
#include <gui/modules/text_input.h>
#include <gui/modules/byte_input.h>
#include "../views/bad_bt_view.h"
#define FILE_BUFFER_LEN 16
typedef enum {
LevelRssi122_100,
LevelRssi99_80,
LevelRssi79_60,
LevelRssi59_40,
LevelRssi39_0,
LevelRssiNum,
LevelRssiError = 0xFF,
} LevelRssiRange;
extern const uint8_t bt_hid_delays[LevelRssiNum];
extern uint8_t bt_timeout;
typedef enum {
BadBtStateInit,
BadBtStateNotConnected,
BadBtStateIdle,
BadBtStateWillRun,
BadBtStateRunning,
BadBtStateDelay,
BadBtStateStringDelay,
BadBtStateWaitForBtn,
BadBtStateDone,
BadBtStateScriptError,
BadBtStateFileError,
} BadBtWorkerState;
struct BadBtState {
BadBtWorkerState state;
uint32_t pin;
uint16_t line_cur;
uint16_t line_nb;
uint32_t delay_remain;
uint16_t error_line;
char error[64];
};
typedef struct BadBtApp BadBtApp;
typedef struct {
FuriHalUsbHidConfig hid_cfg;
FuriThread* thread;
BadBtState st;
FuriString* file_path;
FuriString* keyboard_layout;
uint8_t file_buf[FILE_BUFFER_LEN + 1];
uint8_t buf_start;
uint8_t buf_len;
bool file_end;
uint32_t defdelay;
uint32_t stringdelay;
uint16_t layout[128];
FuriString* line;
FuriString* line_prev;
uint32_t repeat_cnt;
uint8_t key_hold_nb;
bool set_usb_id;
bool set_bt_id;
bool has_usb_id;
bool has_bt_id;
FuriString* string_print;
size_t string_print_pos;
Bt* bt;
BadBtApp* app;
} BadBtScript;
BadBtScript* bad_bt_script_open(FuriString* file_path, Bt* bt, BadBtApp* app);
void bad_bt_script_close(BadBtScript* bad_bt);
void bad_bt_script_set_keyboard_layout(BadBtScript* bad_bt, FuriString* layout_path);
void bad_bt_script_start(BadBtScript* bad_bt);
void bad_bt_script_stop(BadBtScript* bad_bt);
void bad_bt_script_toggle(BadBtScript* bad_bt);
BadBtState* bad_bt_script_get_state(BadBtScript* bad_bt);
#define BAD_BT_ADV_NAME_MAX_LEN FURI_HAL_BT_ADV_NAME_LENGTH
#define BAD_BT_MAC_ADDRESS_LEN GAP_MAC_ADDR_SIZE
// this is the MAC address used when we do not forget paired device (BOUND STATE)
extern const uint8_t BAD_BT_BOUND_MAC_ADDRESS[BAD_BT_MAC_ADDRESS_LEN];
extern const uint8_t BAD_BT_EMPTY_MAC_ADDRESS[BAD_BT_MAC_ADDRESS_LEN];
typedef enum {
BadBtAppErrorNoFiles,
BadBtAppErrorCloseRpc,
} BadBtAppError;
typedef struct {
char bt_name[BAD_BT_ADV_NAME_MAX_LEN];
uint8_t bt_mac[BAD_BT_MAC_ADDRESS_LEN];
GapPairing bt_mode;
} BadBtConfig;
struct BadBtApp {
Gui* gui;
ViewDispatcher* view_dispatcher;
SceneManager* scene_manager;
NotificationApp* notifications;
DialogsApp* dialogs;
Widget* widget;
VariableItemList* var_item_list;
TextInput* text_input;
ByteInput* byte_input;
BadBtAppError error;
FuriString* file_path;
FuriString* keyboard_layout;
BadBt* bad_bt_view;
BadBtScript* bad_bt_script;
Bt* bt;
bool bt_remember;
BadBtConfig config;
BadBtConfig prev_config;
FuriThread* conn_init_thread;
FuriThread* switch_mode_thread;
};
int32_t bad_bt_config_switch_mode(BadBtApp* app);
#ifdef __cplusplus
}
#endif

View file

@ -1,201 +0,0 @@
#include <furi_hal.h>
#include <furi_hal_bt_hid.h>
#include "ducky_script.h"
#include "ducky_script_i.h"
typedef int32_t (*DuckyCmdCallback)(BadBtScript* bad_bt, const char* line, int32_t param);
typedef struct {
char* name;
DuckyCmdCallback callback;
int32_t param;
} DuckyCmd;
static int32_t ducky_fnc_delay(BadBtScript* bad_bt, const char* line, int32_t param) {
UNUSED(param);
line = &line[ducky_get_command_len(line) + 1];
uint32_t delay_val = 0;
bool state = ducky_get_number(line, &delay_val);
if((state) && (delay_val > 0)) {
return (int32_t)delay_val;
}
return ducky_error(bad_bt, "Invalid number %s", line);
}
static int32_t ducky_fnc_defdelay(BadBtScript* bad_bt, const char* line, int32_t param) {
UNUSED(param);
line = &line[ducky_get_command_len(line) + 1];
bool state = ducky_get_number(line, &bad_bt->defdelay);
if(!state) {
return ducky_error(bad_bt, "Invalid number %s", line);
}
return 0;
}
static int32_t ducky_fnc_strdelay(BadBtScript* bad_bt, const char* line, int32_t param) {
UNUSED(param);
line = &line[ducky_get_command_len(line) + 1];
bool state = ducky_get_number(line, &bad_bt->stringdelay);
if(!state) {
return ducky_error(bad_bt, "Invalid number %s", line);
}
return 0;
}
static int32_t ducky_fnc_string(BadBtScript* bad_bt, const char* line, int32_t param) {
line = &line[ducky_get_command_len(line) + 1];
furi_string_set_str(bad_bt->string_print, line);
if(param == 1) {
furi_string_cat(bad_bt->string_print, "\n");
}
if(bad_bt->stringdelay == 0) { // stringdelay not set - run command immidiately
bool state = ducky_string(bad_bt, furi_string_get_cstr(bad_bt->string_print));
if(!state) {
return ducky_error(bad_bt, "Invalid string %s", line);
}
} else { // stringdelay is set - run command in thread to keep handling external events
return SCRIPT_STATE_STRING_START;
}
return 0;
}
static int32_t ducky_fnc_repeat(BadBtScript* bad_bt, const char* line, int32_t param) {
UNUSED(param);
line = &line[ducky_get_command_len(line) + 1];
bool state = ducky_get_number(line, &bad_bt->repeat_cnt);
if((!state) || (bad_bt->repeat_cnt == 0)) {
return ducky_error(bad_bt, "Invalid number %s", line);
}
return 0;
}
static int32_t ducky_fnc_sysrq(BadBtScript* bad_bt, const char* line, int32_t param) {
UNUSED(param);
line = &line[ducky_get_command_len(line) + 1];
uint16_t key = ducky_get_keycode(bad_bt, line, true);
furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN);
furi_hal_bt_hid_kb_press(key);
furi_delay_ms(bt_timeout);
furi_hal_bt_hid_kb_release(key);
furi_hal_bt_hid_kb_release(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN);
return 0;
}
static int32_t ducky_fnc_altchar(BadBtScript* bad_bt, const char* line, int32_t param) {
UNUSED(param);
line = &line[ducky_get_command_len(line) + 1];
ducky_numlock_on(bad_bt);
bool state = ducky_altchar(bad_bt, line);
if(!state) {
return ducky_error(bad_bt, "Invalid altchar %s", line);
}
return 0;
}
static int32_t ducky_fnc_altstring(BadBtScript* bad_bt, const char* line, int32_t param) {
UNUSED(param);
line = &line[ducky_get_command_len(line) + 1];
ducky_numlock_on(bad_bt);
bool state = ducky_altstring(bad_bt, line);
if(!state) {
return ducky_error(bad_bt, "Invalid altstring %s", line);
}
return 0;
}
static int32_t ducky_fnc_hold(BadBtScript* bad_bt, const char* line, int32_t param) {
UNUSED(param);
line = &line[ducky_get_command_len(line) + 1];
uint16_t key = ducky_get_keycode(bad_bt, line, true);
if(key == HID_KEYBOARD_NONE) {
return ducky_error(bad_bt, "No keycode defined for %s", line);
}
bad_bt->key_hold_nb++;
if(bad_bt->key_hold_nb > (HID_KB_MAX_KEYS - 1)) {
return ducky_error(bad_bt, "Too many keys are hold");
}
furi_hal_bt_hid_kb_press(key);
return 0;
}
static int32_t ducky_fnc_release(BadBtScript* bad_bt, const char* line, int32_t param) {
UNUSED(param);
line = &line[ducky_get_command_len(line) + 1];
uint16_t key = ducky_get_keycode(bad_bt, line, true);
if(key == HID_KEYBOARD_NONE) {
return ducky_error(bad_bt, "No keycode defined for %s", line);
}
if(bad_bt->key_hold_nb == 0) {
return ducky_error(bad_bt, "No keys are hold");
}
bad_bt->key_hold_nb--;
furi_hal_bt_hid_kb_release(key);
return 0;
}
static int32_t ducky_fnc_waitforbutton(BadBtScript* bad_bt, const char* line, int32_t param) {
UNUSED(param);
UNUSED(bad_bt);
UNUSED(line);
return SCRIPT_STATE_WAIT_FOR_BTN;
}
static const DuckyCmd ducky_commands[] = {
{"REM", NULL, -1},
{"ID", NULL, -1},
{"BT_ID", NULL, -1},
{"DELAY", ducky_fnc_delay, -1},
{"STRING", ducky_fnc_string, 0},
{"STRINGLN", ducky_fnc_string, 1},
{"DEFAULT_DELAY", ducky_fnc_defdelay, -1},
{"DEFAULTDELAY", ducky_fnc_defdelay, -1},
{"STRINGDELAY", ducky_fnc_strdelay, -1},
{"STRING_DELAY", ducky_fnc_strdelay, -1},
{"REPEAT", ducky_fnc_repeat, -1},
{"SYSRQ", ducky_fnc_sysrq, -1},
{"ALTCHAR", ducky_fnc_altchar, -1},
{"ALTSTRING", ducky_fnc_altstring, -1},
{"ALTCODE", ducky_fnc_altstring, -1},
{"HOLD", ducky_fnc_hold, -1},
{"RELEASE", ducky_fnc_release, -1},
{"WAIT_FOR_BUTTON_PRESS", ducky_fnc_waitforbutton, -1},
};
#define TAG "BadBT"
#define WORKER_TAG TAG "Worker"
int32_t ducky_execute_cmd(BadBtScript* bad_bt, const char* line) {
size_t cmd_word_len = strcspn(line, " ");
for(size_t i = 0; i < COUNT_OF(ducky_commands); i++) {
size_t cmd_compare_len = strlen(ducky_commands[i].name);
if(cmd_compare_len != cmd_word_len) {
continue;
}
if(strncmp(line, ducky_commands[i].name, cmd_compare_len) == 0) {
if(ducky_commands[i].callback == NULL) {
return 0;
} else {
return ((ducky_commands[i].callback)(bad_bt, line, ducky_commands[i].param));
}
}
}
return SCRIPT_STATE_CMD_UNKNOWN;
}

View file

@ -1,44 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <furi.h>
#include <furi_hal.h>
#include "ducky_script.h"
#define SCRIPT_STATE_ERROR (-1)
#define SCRIPT_STATE_END (-2)
#define SCRIPT_STATE_NEXT_LINE (-3)
#define SCRIPT_STATE_CMD_UNKNOWN (-4)
#define SCRIPT_STATE_STRING_START (-5)
#define SCRIPT_STATE_WAIT_FOR_BTN (-6)
uint16_t ducky_get_keycode(BadBtScript* bad_bt, const char* param, bool accept_chars);
uint32_t ducky_get_command_len(const char* line);
bool ducky_is_line_end(const char chr);
uint16_t ducky_get_keycode_by_name(const char* param);
bool ducky_get_number(const char* param, uint32_t* val);
void ducky_numlock_on(BadBtScript* bad_bt);
bool ducky_numpad_press(BadBtScript* bad_bt, const char num);
bool ducky_altchar(BadBtScript* bad_bt, const char* charcode);
bool ducky_altstring(BadBtScript* bad_bt, const char* param);
bool ducky_string(BadBtScript* bad_bt, const char* param);
int32_t ducky_execute_cmd(BadBtScript* bad_bt, const char* line);
int32_t ducky_error(BadBtScript* bad_bt, const char* text, ...);
#ifdef __cplusplus
}
#endif

View file

@ -1,78 +0,0 @@
#include <furi_hal.h>
#include "ducky_script_i.h"
typedef struct {
char* name;
uint16_t keycode;
} DuckyKey;
static const DuckyKey ducky_keys[] = {
{"CTRL-ALT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT},
{"CTRL-SHIFT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT},
{"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT},
{"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI},
{"GUI-SHIFT", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT},
{"GUI-CTRL", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL},
{"CTRL", KEY_MOD_LEFT_CTRL},
{"CONTROL", KEY_MOD_LEFT_CTRL},
{"SHIFT", KEY_MOD_LEFT_SHIFT},
{"ALT", KEY_MOD_LEFT_ALT},
{"GUI", KEY_MOD_LEFT_GUI},
{"WINDOWS", KEY_MOD_LEFT_GUI},
{"DOWNARROW", HID_KEYBOARD_DOWN_ARROW},
{"DOWN", HID_KEYBOARD_DOWN_ARROW},
{"LEFTARROW", HID_KEYBOARD_LEFT_ARROW},
{"LEFT", HID_KEYBOARD_LEFT_ARROW},
{"RIGHTARROW", HID_KEYBOARD_RIGHT_ARROW},
{"RIGHT", HID_KEYBOARD_RIGHT_ARROW},
{"UPARROW", HID_KEYBOARD_UP_ARROW},
{"UP", HID_KEYBOARD_UP_ARROW},
{"ENTER", HID_KEYBOARD_RETURN},
{"BREAK", HID_KEYBOARD_PAUSE},
{"PAUSE", HID_KEYBOARD_PAUSE},
{"CAPSLOCK", HID_KEYBOARD_CAPS_LOCK},
{"DELETE", HID_KEYBOARD_DELETE_FORWARD},
{"BACKSPACE", HID_KEYBOARD_DELETE},
{"END", HID_KEYBOARD_END},
{"ESC", HID_KEYBOARD_ESCAPE},
{"ESCAPE", HID_KEYBOARD_ESCAPE},
{"HOME", HID_KEYBOARD_HOME},
{"INSERT", HID_KEYBOARD_INSERT},
{"NUMLOCK", HID_KEYPAD_NUMLOCK},
{"PAGEUP", HID_KEYBOARD_PAGE_UP},
{"PAGEDOWN", HID_KEYBOARD_PAGE_DOWN},
{"PRINTSCREEN", HID_KEYBOARD_PRINT_SCREEN},
{"SCROLLLOCK", HID_KEYBOARD_SCROLL_LOCK},
{"SPACE", HID_KEYBOARD_SPACEBAR},
{"TAB", HID_KEYBOARD_TAB},
{"MENU", HID_KEYBOARD_APPLICATION},
{"APP", HID_KEYBOARD_APPLICATION},
{"F1", HID_KEYBOARD_F1},
{"F2", HID_KEYBOARD_F2},
{"F3", HID_KEYBOARD_F3},
{"F4", HID_KEYBOARD_F4},
{"F5", HID_KEYBOARD_F5},
{"F6", HID_KEYBOARD_F6},
{"F7", HID_KEYBOARD_F7},
{"F8", HID_KEYBOARD_F8},
{"F9", HID_KEYBOARD_F9},
{"F10", HID_KEYBOARD_F10},
{"F11", HID_KEYBOARD_F11},
{"F12", HID_KEYBOARD_F12},
};
uint16_t ducky_get_keycode_by_name(const char* param) {
for(size_t i = 0; i < COUNT_OF(ducky_keys); i++) {
size_t key_cmd_len = strlen(ducky_keys[i].name);
if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) &&
(ducky_is_line_end(param[key_cmd_len]))) {
return ducky_keys[i].keycode;
}
}
return HID_KEYBOARD_NONE;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 576 B

View file

@ -1,30 +0,0 @@
#include "bad_bt_scene.h"
// Generate scene on_enter handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const bad_bt_scene_on_enter_handlers[])(void*) = {
#include "bad_bt_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_event handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
bool (*const bad_bt_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
#include "bad_bt_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_exit handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
void (*const bad_bt_scene_on_exit_handlers[])(void* context) = {
#include "bad_bt_scene_config.h"
};
#undef ADD_SCENE
// Initialize scene handlers configuration structure
const SceneManagerHandlers bad_bt_scene_handlers = {
.on_enter_handlers = bad_bt_scene_on_enter_handlers,
.on_event_handlers = bad_bt_scene_on_event_handlers,
.on_exit_handlers = bad_bt_scene_on_exit_handlers,
.scene_num = BadBtSceneNum,
};

View file

@ -1,29 +0,0 @@
#pragma once
#include <gui/scene_manager.h>
// Generate scene id and total number
#define ADD_SCENE(prefix, name, id) BadBtScene##id,
typedef enum {
#include "bad_bt_scene_config.h"
BadBtSceneNum,
} BadBtScene;
#undef ADD_SCENE
extern const SceneManagerHandlers bad_bt_scene_handlers;
// Generate scene on_enter handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
#include "bad_bt_scene_config.h"
#undef ADD_SCENE
// Generate scene on_event handlers declaration
#define ADD_SCENE(prefix, name, id) \
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
#include "bad_bt_scene_config.h"
#undef ADD_SCENE
// Generate scene on_exit handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
#include "bad_bt_scene_config.h"
#undef ADD_SCENE

View file

@ -1,104 +0,0 @@
#include "../bad_bt_app.h"
#include "../helpers/ducky_script.h"
#include "furi_hal_power.h"
enum VarItemListIndex {
VarItemListIndexKeyboardLayout,
VarItemListIndexBtRemember,
VarItemListIndexBtDeviceName,
VarItemListIndexBtMacAddress,
VarItemListIndexRandomizeBtMac,
};
void bad_bt_scene_config_bt_remember_callback(VariableItem* item) {
BadBtApp* bad_bt = variable_item_get_context(item);
bad_bt->bt_remember = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, bad_bt->bt_remember ? "ON" : "OFF");
view_dispatcher_send_custom_event(bad_bt->view_dispatcher, VarItemListIndexBtRemember);
}
void bad_bt_scene_config_var_item_list_callback(void* context, uint32_t index) {
BadBtApp* bad_bt = context;
view_dispatcher_send_custom_event(bad_bt->view_dispatcher, index);
}
void bad_bt_scene_config_on_enter(void* context) {
BadBtApp* bad_bt = context;
VariableItemList* var_item_list = bad_bt->var_item_list;
VariableItem* item;
item = variable_item_list_add(var_item_list, "Keyboard layout", 0, NULL, bad_bt);
item = variable_item_list_add(
var_item_list, "BT Remember", 2, bad_bt_scene_config_bt_remember_callback, bad_bt);
variable_item_set_current_value_index(item, bad_bt->bt_remember);
variable_item_set_current_value_text(item, bad_bt->bt_remember ? "ON" : "OFF");
item = variable_item_list_add(var_item_list, "BT Device Name", 0, NULL, bad_bt);
if(bad_bt->bad_bt_script->set_bt_id) {
variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset Name!");
}
item = variable_item_list_add(var_item_list, "BT MAC Address", 0, NULL, bad_bt);
if(bad_bt->bt_remember) {
variable_item_set_locked(item, true, "Remember\nmust be Off!");
} else if(bad_bt->bad_bt_script->set_bt_id) {
variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset MAC!");
}
item = variable_item_list_add(var_item_list, "Randomize BT MAC", 0, NULL, bad_bt);
if(bad_bt->bt_remember) {
variable_item_set_locked(item, true, "Remember\nmust be Off!");
} else if(bad_bt->bad_bt_script->set_bt_id) {
variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset MAC!");
}
variable_item_list_set_enter_callback(
var_item_list, bad_bt_scene_config_var_item_list_callback, bad_bt);
variable_item_list_set_selected_item(
var_item_list, scene_manager_get_scene_state(bad_bt->scene_manager, BadBtSceneConfig));
view_dispatcher_switch_to_view(bad_bt->view_dispatcher, BadBtAppViewConfig);
}
bool bad_bt_scene_config_on_event(void* context, SceneManagerEvent event) {
BadBtApp* bad_bt = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
scene_manager_set_scene_state(bad_bt->scene_manager, BadBtSceneConfig, event.event);
consumed = true;
switch(event.event) {
case VarItemListIndexKeyboardLayout:
scene_manager_next_scene(bad_bt->scene_manager, BadBtSceneConfigLayout);
break;
case VarItemListIndexBtRemember:
bad_bt_config_switch_remember_mode(bad_bt);
scene_manager_previous_scene(bad_bt->scene_manager);
scene_manager_next_scene(bad_bt->scene_manager, BadBtSceneConfig);
break;
case VarItemListIndexBtDeviceName:
scene_manager_next_scene(bad_bt->scene_manager, BadBtSceneConfigName);
break;
case VarItemListIndexBtMacAddress:
scene_manager_next_scene(bad_bt->scene_manager, BadBtSceneConfigMac);
break;
case VarItemListIndexRandomizeBtMac:
furi_hal_random_fill_buf(bad_bt->config.bt_mac, BAD_BT_MAC_ADDRESS_LEN);
bt_set_profile_mac_address(bad_bt->bt, bad_bt->config.bt_mac);
break;
default:
break;
}
}
return consumed;
}
void bad_bt_scene_config_on_exit(void* context) {
BadBtApp* bad_bt = context;
VariableItemList* var_item_list = bad_bt->var_item_list;
variable_item_list_reset(var_item_list);
}

View file

@ -1,7 +0,0 @@
ADD_SCENE(bad_bt, file_select, FileSelect)
ADD_SCENE(bad_bt, work, Work)
ADD_SCENE(bad_bt, error, Error)
ADD_SCENE(bad_bt, config, Config)
ADD_SCENE(bad_bt, config_layout, ConfigLayout)
ADD_SCENE(bad_bt, config_name, ConfigName)
ADD_SCENE(bad_bt, config_mac, ConfigMac)

View file

@ -1,47 +0,0 @@
#include "../bad_bt_app.h"
#include "furi_hal_power.h"
#include <storage/storage.h>
static bool bad_bt_layout_select(BadBtApp* bad_bt) {
furi_assert(bad_bt);
FuriString* predefined_path;
predefined_path = furi_string_alloc();
if(!furi_string_empty(bad_bt->keyboard_layout)) {
furi_string_set(predefined_path, bad_bt->keyboard_layout);
} else {
furi_string_set(predefined_path, BAD_BT_APP_PATH_LAYOUT_FOLDER);
}
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(
&browser_options, BAD_BT_APP_LAYOUT_EXTENSION, &I_keyboard_10px);
browser_options.base_path = BAD_BT_APP_PATH_LAYOUT_FOLDER;
browser_options.skip_assets = false;
// Input events and views are managed by file_browser
bool res = dialog_file_browser_show(
bad_bt->dialogs, bad_bt->keyboard_layout, predefined_path, &browser_options);
furi_string_free(predefined_path);
return res;
}
void bad_bt_scene_config_layout_on_enter(void* context) {
BadBtApp* bad_bt = context;
if(bad_bt_layout_select(bad_bt)) {
bad_bt_script_set_keyboard_layout(bad_bt->bad_bt_script, bad_bt->keyboard_layout);
}
scene_manager_previous_scene(bad_bt->scene_manager);
}
bool bad_bt_scene_config_layout_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void bad_bt_scene_config_layout_on_exit(void* context) {
UNUSED(context);
}

View file

@ -1,52 +0,0 @@
#include "../bad_bt_app.h"
#define TAG "BadBtConfigMac"
void bad_bt_scene_config_mac_byte_input_callback(void* context) {
BadBtApp* bad_bt = context;
view_dispatcher_send_custom_event(bad_bt->view_dispatcher, BadBtAppCustomEventByteInputDone);
}
void bad_bt_scene_config_mac_on_enter(void* context) {
BadBtApp* bad_bt = context;
furi_hal_bt_reverse_mac_addr(bad_bt->config.bt_mac);
// Setup view
ByteInput* byte_input = bad_bt->byte_input;
byte_input_set_header_text(byte_input, "Set BT MAC address");
byte_input_set_result_callback(
byte_input,
bad_bt_scene_config_mac_byte_input_callback,
NULL,
bad_bt,
bad_bt->config.bt_mac,
GAP_MAC_ADDR_SIZE);
view_dispatcher_switch_to_view(bad_bt->view_dispatcher, BadBtAppViewConfigMac);
}
bool bad_bt_scene_config_mac_on_event(void* context, SceneManagerEvent event) {
BadBtApp* bad_bt = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == BadBtAppCustomEventByteInputDone) {
scene_manager_previous_scene(bad_bt->scene_manager);
consumed = true;
}
}
return consumed;
}
void bad_bt_scene_config_mac_on_exit(void* context) {
BadBtApp* bad_bt = context;
furi_hal_bt_reverse_mac_addr(bad_bt->config.bt_mac);
bt_set_profile_mac_address(bad_bt->bt, bad_bt->config.bt_mac);
// Clear view
byte_input_set_result_callback(bad_bt->byte_input, NULL, NULL, NULL, NULL, 0);
byte_input_set_header_text(bad_bt->byte_input, "");
}

View file

@ -1,45 +0,0 @@
#include "../bad_bt_app.h"
static void bad_bt_scene_config_name_text_input_callback(void* context) {
BadBtApp* bad_bt = context;
view_dispatcher_send_custom_event(bad_bt->view_dispatcher, BadBtAppCustomEventTextEditResult);
}
void bad_bt_scene_config_name_on_enter(void* context) {
BadBtApp* bad_bt = context;
TextInput* text_input = bad_bt->text_input;
text_input_set_header_text(text_input, "Set BT device name");
text_input_set_result_callback(
text_input,
bad_bt_scene_config_name_text_input_callback,
bad_bt,
bad_bt->config.bt_name,
BAD_BT_ADV_NAME_MAX_LEN,
true);
view_dispatcher_switch_to_view(bad_bt->view_dispatcher, BadBtAppViewConfigName);
}
bool bad_bt_scene_config_name_on_event(void* context, SceneManagerEvent event) {
BadBtApp* bad_bt = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
consumed = true;
if(event.event == BadBtAppCustomEventTextEditResult) {
bt_set_profile_adv_name(bad_bt->bt, bad_bt->config.bt_name);
}
scene_manager_previous_scene(bad_bt->scene_manager);
}
return consumed;
}
void bad_bt_scene_config_name_on_exit(void* context) {
BadBtApp* bad_bt = context;
TextInput* text_input = bad_bt->text_input;
text_input_reset(text_input);
}

View file

@ -1,61 +0,0 @@
#include "../bad_bt_app.h"
static void
bad_bt_scene_error_event_callback(GuiButtonType result, InputType type, void* context) {
furi_assert(context);
BadBtApp* app = context;
if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) {
view_dispatcher_send_custom_event(app->view_dispatcher, BadBtCustomEventErrorBack);
}
}
void bad_bt_scene_error_on_enter(void* context) {
BadBtApp* app = context;
if(app->error == BadBtAppErrorNoFiles) {
widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43);
widget_add_string_multiline_element(
app->widget,
81,
4,
AlignCenter,
AlignTop,
FontSecondary,
"No SD card or\napp data found.\nThis app will not\nwork without\nrequired files.");
widget_add_button_element(
app->widget, GuiButtonTypeLeft, "Back", bad_bt_scene_error_event_callback, app);
} else if(app->error == BadBtAppErrorCloseRpc) {
widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64);
widget_add_string_multiline_element(
app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!");
widget_add_string_multiline_element(
app->widget,
3,
30,
AlignLeft,
AlignTop,
FontSecondary,
"Disconnect from\nPC or phone to\nuse this function.");
}
view_dispatcher_switch_to_view(app->view_dispatcher, BadBtAppViewError);
}
bool bad_bt_scene_error_on_event(void* context, SceneManagerEvent event) {
BadBtApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == BadBtCustomEventErrorBack) {
view_dispatcher_stop(app->view_dispatcher);
consumed = true;
}
}
return consumed;
}
void bad_bt_scene_error_on_exit(void* context) {
BadBtApp* app = context;
widget_reset(app->widget);
}

View file

@ -1,49 +0,0 @@
#include "../bad_bt_app.h"
#include <furi_hal_power.h>
#include <storage/storage.h>
static bool bad_bt_file_select(BadBtApp* bad_bt) {
furi_assert(bad_bt);
DialogsFileBrowserOptions browser_options;
dialog_file_browser_set_basic_options(
&browser_options, BAD_BT_APP_SCRIPT_EXTENSION, &I_badbt_10px);
browser_options.base_path = BAD_BT_APP_BASE_FOLDER;
browser_options.skip_assets = true;
// Input events and views are managed by file_browser
bool res = dialog_file_browser_show(
bad_bt->dialogs, bad_bt->file_path, bad_bt->file_path, &browser_options);
return res;
}
void bad_bt_scene_file_select_on_enter(void* context) {
BadBtApp* bad_bt = context;
if(bad_bt->bad_bt_script) {
bad_bt_script_close(bad_bt->bad_bt_script);
bad_bt->bad_bt_script = NULL;
}
if(bad_bt_file_select(bad_bt)) {
bad_bt->bad_bt_script = bad_bt_script_open(bad_bt->file_path, bad_bt->bt, bad_bt);
bad_bt_script_set_keyboard_layout(bad_bt->bad_bt_script, bad_bt->keyboard_layout);
scene_manager_next_scene(bad_bt->scene_manager, BadBtSceneWork);
} else {
view_dispatcher_stop(bad_bt->view_dispatcher);
}
}
bool bad_bt_scene_file_select_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
// BadBtApp* bad_bt = context;
return false;
}
void bad_bt_scene_file_select_on_exit(void* context) {
UNUSED(context);
// BadBtApp* bad_bt = context;
}

View file

@ -1,56 +0,0 @@
#include "../helpers/ducky_script.h"
#include "../bad_bt_app.h"
#include "../views/bad_bt_view.h"
#include <furi_hal.h>
#include "toolbox/path.h"
void bad_bt_scene_work_button_callback(InputKey key, void* context) {
furi_assert(context);
BadBtApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, key);
}
bool bad_bt_scene_work_on_event(void* context, SceneManagerEvent event) {
BadBtApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == InputKeyLeft) {
if(bad_bt_is_idle_state(app->bad_bt_view)) {
scene_manager_next_scene(app->scene_manager, BadBtSceneConfig);
}
consumed = true;
} else if(event.event == InputKeyOk) {
bad_bt_script_toggle(app->bad_bt_script);
consumed = true;
}
} else if(event.type == SceneManagerEventTypeTick) {
bad_bt_set_state(app->bad_bt_view, bad_bt_script_get_state(app->bad_bt_script));
}
return consumed;
}
void bad_bt_scene_work_on_enter(void* context) {
BadBtApp* app = context;
FuriString* file_name;
file_name = furi_string_alloc();
path_extract_filename(app->file_path, file_name, true);
bad_bt_set_file_name(app->bad_bt_view, furi_string_get_cstr(file_name));
furi_string_free(file_name);
FuriString* layout;
layout = furi_string_alloc();
path_extract_filename(app->keyboard_layout, layout, true);
bad_bt_set_layout(app->bad_bt_view, furi_string_get_cstr(layout));
furi_string_free(layout);
bad_bt_set_state(app->bad_bt_view, bad_bt_script_get_state(app->bad_bt_script));
bad_bt_set_button_callback(app->bad_bt_view, bad_bt_scene_work_button_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, BadBtAppViewWork);
}
void bad_bt_scene_work_on_exit(void* context) {
UNUSED(context);
}

View file

@ -1,233 +0,0 @@
#include "bad_bt_view.h"
#include "../helpers/ducky_script.h"
#include "../bad_bt_app.h"
#include <toolbox/path.h>
#include <gui/elements.h>
#include <assets_icons.h>
#define MAX_NAME_LEN 64
typedef struct {
char file_name[MAX_NAME_LEN];
char layout[MAX_NAME_LEN];
BadBtState state;
uint8_t anim_frame;
} BadBtModel;
static void bad_bt_draw_callback(Canvas* canvas, void* _model) {
BadBtModel* model = _model;
FuriString* disp_str;
disp_str = furi_string_alloc_set("(BT) ");
furi_string_cat_str(disp_str, model->file_name);
elements_string_fit_width(canvas, disp_str, 128 - 2);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str));
if(strlen(model->layout) == 0) {
furi_string_set(disp_str, "(default)");
} else {
furi_string_reset(disp_str);
furi_string_push_back(disp_str, '(');
for(size_t i = 0; i < strlen(model->layout); i++)
furi_string_push_back(disp_str, model->layout[i]);
furi_string_push_back(disp_str, ')');
}
if(model->state.pin) {
furi_string_cat_printf(disp_str, " PIN: %ld", model->state.pin);
}
elements_string_fit_width(canvas, disp_str, 128 - 2);
canvas_draw_str(
canvas, 2, 8 + canvas_current_font_height(canvas), furi_string_get_cstr(disp_str));
furi_string_reset(disp_str);
if((model->state.state == BadBtStateIdle) || (model->state.state == BadBtStateDone) ||
(model->state.state == BadBtStateNotConnected)) {
elements_button_center(canvas, "Run");
elements_button_left(canvas, "Config");
} else if((model->state.state == BadBtStateRunning) || (model->state.state == BadBtStateDelay)) {
elements_button_center(canvas, "Stop");
} else if(model->state.state == BadBtStateWaitForBtn) {
elements_button_center(canvas, "Press to continue");
} else if(model->state.state == BadBtStateWillRun) {
elements_button_center(canvas, "Cancel");
}
if(model->state.state == BadBtStateNotConnected) {
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect to");
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "a device");
} else if(model->state.state == BadBtStateWillRun) {
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run");
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "on connect");
} else if(model->state.state == BadBtStateFileError) {
canvas_draw_icon(canvas, 4, 26, &I_Error_18x18);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "File");
canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "ERROR");
} else if(model->state.state == BadBtStateScriptError) {
canvas_draw_icon(canvas, 4, 26, &I_Error_18x18);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:");
canvas_set_font(canvas, FontSecondary);
furi_string_printf(disp_str, "line %u", model->state.error_line);
canvas_draw_str_aligned(
canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
furi_string_reset(disp_str);
furi_string_set_str(disp_str, model->state.error);
elements_string_fit_width(canvas, disp_str, canvas_width(canvas));
canvas_draw_str_aligned(
canvas, 127, 56, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
furi_string_reset(disp_str);
} else if(model->state.state == BadBtStateIdle) {
canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18);
canvas_set_font(canvas, FontBigNumbers);
canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "0");
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
} else if(model->state.state == BadBtStateRunning) {
if(model->anim_frame == 0) {
canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21);
} else {
canvas_draw_icon(canvas, 4, 23, &I_EviSmile2_18x21);
}
canvas_set_font(canvas, FontBigNumbers);
furi_string_printf(
disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb);
canvas_draw_str_aligned(
canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
furi_string_reset(disp_str);
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
} else if(model->state.state == BadBtStateDone) {
canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21);
canvas_set_font(canvas, FontBigNumbers);
canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "100");
furi_string_reset(disp_str);
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
} else if(model->state.state == BadBtStateDelay) {
if(model->anim_frame == 0) {
canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21);
} else {
canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21);
}
canvas_set_font(canvas, FontBigNumbers);
furi_string_printf(
disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb);
canvas_draw_str_aligned(
canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
furi_string_reset(disp_str);
canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14);
canvas_set_font(canvas, FontSecondary);
furi_string_printf(disp_str, "delay %lus", model->state.delay_remain);
canvas_draw_str_aligned(
canvas, 127, 50, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
furi_string_reset(disp_str);
} else {
canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18);
}
furi_string_free(disp_str);
}
static bool bad_bt_input_callback(InputEvent* event, void* context) {
furi_assert(context);
BadBt* bad_bt = context;
bool consumed = false;
if(event->type == InputTypeShort) {
if((event->key == InputKeyLeft) || (event->key == InputKeyOk)) {
consumed = true;
furi_assert(bad_bt->callback);
bad_bt->callback(event->key, bad_bt->context);
}
}
return consumed;
}
BadBt* bad_bt_alloc() {
BadBt* bad_bt = malloc(sizeof(BadBt));
bad_bt->view = view_alloc();
view_allocate_model(bad_bt->view, ViewModelTypeLocking, sizeof(BadBtModel));
view_set_context(bad_bt->view, bad_bt);
view_set_draw_callback(bad_bt->view, bad_bt_draw_callback);
view_set_input_callback(bad_bt->view, bad_bt_input_callback);
return bad_bt;
}
void bad_bt_free(BadBt* bad_bt) {
furi_assert(bad_bt);
view_free(bad_bt->view);
free(bad_bt);
}
View* bad_bt_get_view(BadBt* bad_bt) {
furi_assert(bad_bt);
return bad_bt->view;
}
void bad_bt_set_button_callback(BadBt* bad_bt, BadBtButtonCallback callback, void* context) {
furi_assert(bad_bt);
furi_assert(callback);
with_view_model(
bad_bt->view,
BadBtModel * model,
{
UNUSED(model);
bad_bt->callback = callback;
bad_bt->context = context;
},
true);
}
void bad_bt_set_file_name(BadBt* bad_bt, const char* name) {
furi_assert(name);
with_view_model(
bad_bt->view, BadBtModel * model, { strlcpy(model->file_name, name, MAX_NAME_LEN); }, true);
}
void bad_bt_set_layout(BadBt* bad_bt, const char* layout) {
furi_assert(layout);
with_view_model(
bad_bt->view, BadBtModel * model, { strlcpy(model->layout, layout, MAX_NAME_LEN); }, true);
}
void bad_bt_set_state(BadBt* bad_bt, BadBtState* st) {
furi_assert(st);
uint32_t pin = 0;
if(bad_bt->context != NULL) {
BadBtApp* app = bad_bt->context;
if(app->bt != NULL) {
pin = app->bt->pin;
}
}
st->pin = pin;
with_view_model(
bad_bt->view,
BadBtModel * model,
{
memcpy(&(model->state), st, sizeof(BadBtState));
model->anim_frame ^= 1;
},
true);
}
bool bad_bt_is_idle_state(BadBt* bad_bt) {
bool is_idle = false;
with_view_model(
bad_bt->view,
BadBtModel * model,
{
if((model->state.state == BadBtStateIdle) || (model->state.state == BadBtStateDone) ||
(model->state.state == BadBtStateNotConnected)) {
is_idle = true;
}
},
false);
return is_idle;
}

Some files were not shown because too many files have changed in this diff Show more