├── .gitignore ├── .gitmodules ├── 99-pok3r.rules ├── updatepackage.h ├── test.sh ├── kbproto.cpp ├── .gitlab-ci.yml ├── README.md ├── rawhid ├── hiddevice.h ├── CMakeLists.txt ├── hid.h ├── rawhid_test.c ├── hiddevice.cpp ├── hid_WINDOWS.c ├── hid_LINUX.c └── hid_MACOSX.c ├── keymaps ├── vortex_core.json ├── ansi60.json ├── ansi60_sprs.json ├── ansi60_spls.json ├── ansi60_splsrs.json └── ansi80.json ├── LICENSE ├── kbscan.h ├── kbproto.h ├── keymap.h ├── FindLibUSB.cmake ├── proto_qmk.h ├── proto_cykb.h ├── proto_pok3r.h ├── FindLibUSB-1.0.cmake ├── CMakeLists.txt ├── proto_qmk.cpp ├── proto_pok3r.cpp ├── kbscan.cpp ├── proto_cykb.cpp ├── main.cpp └── updatepackage.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.user 2 | *.user.* 3 | 4 | *.swp 5 | 6 | gen_keymaps.h 7 | 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libchaos"] 2 | path = libchaos 3 | url = https://gitlab.com/ChaoticEnigma/libchaos.git 4 | branch = patches/pok3r 5 | [submodule "nlohmann_json"] 6 | path = nlohmann_json 7 | url = https://github.com/nlohmann/json 8 | -------------------------------------------------------------------------------- /99-pok3r.rules: -------------------------------------------------------------------------------- 1 | 2 | # Holtek Raw HID Interfaces 3 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="04d9", MODE="0666" 4 | #SUBSYSTEMS=="usb", ATTRS{idVendor}=="04d9", GROUP="usb" 5 | #SUBSYSTEMS=="usb", ATTRS{idVendor}=="04d9", ATTRS{bInterfaceClass}=="03", MODE="0666" 6 | #SUBSYSTEMS=="usb", ATTRS{idVendor}=="04d9", ATTRS{bInterfaceClass}=="03", ATTRS{bInterfaceSubClass}=="00", MODE="0666" 7 | 8 | -------------------------------------------------------------------------------- /updatepackage.h: -------------------------------------------------------------------------------- 1 | #ifndef UPDATEPACKAGE_H 2 | #define UPDATEPACKAGE_H 3 | 4 | #include "zbinary.h" 5 | #include "zpath.h" 6 | #include "zfile.h" 7 | 8 | using namespace LibChaos; 9 | 10 | class UpdatePackage { 11 | public: 12 | UpdatePackage(); 13 | 14 | bool loadFromExe(ZPath exe, int index); 15 | 16 | const ZBinary &getFirmware() const; 17 | 18 | private: 19 | ZBinary firmware; 20 | }; 21 | 22 | #endif // UPDATEPACKAGE_H 23 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | mkdir -p output 4 | 5 | ./pok3rtool decode $1/vendor/vortex/POK3R_V117.exe output/pok3r_v117.bin 6 | ./pok3rtool decode $1/vendor/vortex/POK3R_RGB_V140.exe output/pok3r_rgb_v140.bin 7 | ./pok3rtool decode $1/vendor/vortex/CORE_V145.exe output/core_v145.bin 8 | ./pok3rtool decode $1/vendor/vortex/RACE_V124.exe output/race_v124.bin 9 | ./pok3rtool decode $1/vendor/vortex/VIBE_V113.exe output/vibe_v113.bin 10 | 11 | ./pok3rtool decode $1/vendor/kbp/cykb112_v107.exe output/kvp60_v107.bin 12 | ./pok3rtool decode $1/vendor/kbp/cykb129_v106.exe output/kvp80_v129.bin 13 | 14 | ./pok3rtool decode $1/vendor/tex/AP_0163_1.01.01r.exe output/yoda_v101.bin 15 | 16 | -------------------------------------------------------------------------------- /kbproto.cpp: -------------------------------------------------------------------------------- 1 | #include "kbproto.h" 2 | 3 | #include "zlog.h" 4 | 5 | KBProto::KBProto(KBType type) : _type(type){ 6 | 7 | } 8 | 9 | bool KBProto::update(ZString version, const ZBinary &fwbin){ 10 | // Reset to bootloader 11 | if(!rebootBootloader()) 12 | return false; 13 | 14 | LOG("Current Version: " << getVersion()); 15 | 16 | auto status = clearVersion(); 17 | if(status != SUCCESS) 18 | return status; 19 | 20 | if(!writeFirmware(fwbin)) 21 | return false; 22 | 23 | status = setVersion(version); 24 | if(status != SUCCESS) 25 | return status; 26 | 27 | // Reset to firmware 28 | if(!rebootFirmware(false)) 29 | return false; 30 | return true; 31 | } 32 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - build 3 | - test 4 | 5 | pok3rtool:linux: 6 | stage: build 7 | variables: 8 | GIT_SUBMODULE_STRATEGY: recursive 9 | tags: 10 | - linux 11 | image: fedora 12 | before_script: 13 | - dnf install -y gcc gcc-c++ cmake make openssl-devel libusb-devel 14 | script: 15 | - mkdir build 16 | - cd build 17 | - cmake -D CMAKE_BUILD_TYPE="Release" .. 18 | - make 19 | artifacts: 20 | name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" 21 | paths: 22 | - build/pok3rtool 23 | - build/libpok3rlib.so 24 | - build/rawhid/librawhid.so 25 | - build/libchaos/chaos/libchaos.so 26 | 27 | test:linux: 28 | stage: test 29 | dependencies: 30 | - pok3rtool:linux 31 | variables: 32 | GIT_STRATEGY: none 33 | tags: 34 | - linux 35 | image: fedora 36 | before_script: 37 | - dnf install -y libstdc++ openssl libusb 38 | script: 39 | - cd build 40 | - ./pok3rtool list 41 | 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pok3rtool Vortex Keyboard Dev Tool/Library 2 | 3 | *Disclaimer: This project comes with no warranty, and may be used for free at your own risk.* 4 | 5 | The `pok3rtool` CLI application runs on Linux, Windows, and Mac OS X. Along with some development 6 | tools, `pok3rtool` implements the firmware update protocol over USB for the POK3R, POK3R RGB, 7 | Vortex Core, RACE3, ViBE, and some others. 8 | 9 | The `pok3rtool` executable is built with CMake. You will need all submodules checked out to build it. 10 | 11 | **WARNING: THIS TOOL CAN VERY EASILY BRICK YOUR KEYBOARD IF USED INCORRECTLY, MAKING IT 12 | UNUSABLE WITHOUT EXPENSIVE DEVELOPMENT TOOLS. READ THE DOCUMENTATION, POSSIBLY READ THE 13 | CODE, AND PROCEED AT YOUR OWN RISK.** 14 | 15 | [See the wiki for pok3rtool usage, warnings, etc.](https://github.com/pok3r-custom/pok3rtool/wiki) 16 | 17 | ### Building 18 | 19 | git clone --recursive https://gitlab.com/pok3r-custom/pok3rtool.git 20 | mkdir pok3rtool-build 21 | cd pok3rtool-build 22 | cmake ../pok3rtool 23 | make 24 | 25 | -------------------------------------------------------------------------------- /rawhid/hiddevice.h: -------------------------------------------------------------------------------- 1 | #ifndef HIDDEVICE_H 2 | #define HIDDEVICE_H 3 | 4 | #include "hid.h" 5 | #include 6 | 7 | #include "zbinary.h" 8 | #include "zpointer.h" 9 | using namespace LibChaos; 10 | 11 | #define SEND_TIMEOUT 200 12 | #define RECV_TIMEOUT 200 13 | #define RECV_TIMEOUT_MAX 1000 14 | 15 | struct rawhid_detail; 16 | 17 | class HIDDevice { 18 | public: 19 | typedef bool (*filter_func_type)(zu16 vid, zu16 pid, zu16 upage, zu16 usage); 20 | public: 21 | HIDDevice(); 22 | HIDDevice(hid_t *hidt); 23 | 24 | HIDDevice(const HIDDevice &other) = delete; 25 | ~HIDDevice(); 26 | 27 | bool open(zu16 vid, zu16 pid, zu16 usage_page, zu16 usage); 28 | void close(); 29 | bool isOpen() const; 30 | 31 | bool send(const ZBinary &data, bool tolerate_dc = false); 32 | bool recv(ZBinary &data); 33 | 34 | static ZArray> openAll(zu16 vid, zu16 pid, zu16 usage_page, zu16 usage); 35 | 36 | static zu32 openFilter(std::function func); 37 | 38 | private: 39 | hid_t *hid; 40 | }; 41 | 42 | #endif // HIDDEVICE_H 43 | -------------------------------------------------------------------------------- /keymaps/vortex_core.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "vortex_core", 3 | 4 | "layout" : [ 5 | [ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 ], 6 | [ 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 7 ], 7 | [ 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 4 ], 8 | [ 5, 4, 4, 4, 7, 11, 4, 4, 4, 5 ] 9 | ], 10 | 11 | "layers" : [ 12 | [ 13 | "ESC", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "+", "BACK", 14 | "TAB", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "[", "]", "|", 15 | "CAPS", "A", "S", "D", "F", "G", "H", "J", "K", "L", ";", "'", "ENTER", 16 | "SHIFT", "Z", "X", "C", "V", "B", "N", "M", ",", ".", "/", "SHIFT", 17 | "CTRL", "META", "ALT", "SPACE", "ALT", "FN", "MENU", "CTRL" 18 | ], 19 | [ 20 | "`", "", "", "", "", "", "", "", "", "", "", "", "", "", 21 | "", "", "", "", "", "", "", "", "UP", "", "", "", "", "", 22 | "", "", "", "", "", "", "", "LEFT", "DOWN", "RIGHT", "", "", "", 23 | "", "", "", "", "", "", "", "", "", "", "", "", 24 | "", "", "", "", "", "", "", "" 25 | ] 26 | ] 27 | } 28 | 29 | -------------------------------------------------------------------------------- /keymaps/ansi60.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "ansi60", 3 | 4 | "layout" : [ 5 | [ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8 ], 6 | [ 6, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6 ], 7 | [ 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 9 ], 8 | [ 9, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 11 ], 9 | [ 5, 5, 5, 25, 5, 5, 5, 5 ] 10 | ], 11 | 12 | "layers" : [ 13 | [ 14 | "ESC", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "+", "BACK", 15 | "TAB", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "[", "]", "|", 16 | "CAPS", "A", "S", "D", "F", "G", "H", "J", "K", "L", ";", "'", "ENTER", 17 | "SHIFT", "Z", "X", "C", "V", "B", "N", "M", ",", ".", "/", "SHIFT", 18 | "CTRL", "META", "ALT", "SPACE", "ALT", "FN", "MENU", "CTRL" 19 | ], 20 | [ 21 | "`", "", "", "", "", "", "", "", "", "", "", "", "", "", 22 | "", "", "", "", "", "", "", "", "UP", "", "", "", "", "", 23 | "", "", "", "", "", "", "", "LEFT", "DOWN", "RIGHT", "", "", "", 24 | "", "", "", "", "", "", "", "", "", "", "", "", 25 | "", "", "", "", "", "", "", "" 26 | ] 27 | ] 28 | } 29 | 30 | -------------------------------------------------------------------------------- /keymaps/ansi60_sprs.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "ansi60_sprs", 3 | 4 | "layout" : [ 5 | [ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8 ], 6 | [ 6, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6 ], 7 | [ 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 9 ], 8 | [ 9, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 7, 4], 9 | [ 5, 5, 5, 25, 5, 5, 5, 5 ] 10 | ], 11 | 12 | "layers" : [ 13 | [ 14 | "ESC", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "+", "BACK", 15 | "TAB", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "[", "]", "|", 16 | "CAPS", "A", "S", "D", "F", "G", "H", "J", "K", "L", ";", "'", "ENTER", 17 | "SHIFT", "Z", "X", "C", "V", "B", "N", "M", ",", ".", "/", "SHIFT", "", 18 | "CTRL", "META", "ALT", "SPACE", "ALT", "FN", "MENU", "CTRL" 19 | ], 20 | [ 21 | "`", "", "", "", "", "", "", "", "", "", "", "", "", "", 22 | "", "", "", "", "", "", "", "", "UP", "", "", "", "", "", 23 | "", "", "", "", "", "", "", "LEFT", "DOWN", "RIGHT", "", "", "", 24 | "", "", "", "", "", "", "", "", "", "", "", "", "", 25 | "", "", "", "", "", "", "", "" 26 | ] 27 | ] 28 | } 29 | 30 | -------------------------------------------------------------------------------- /keymaps/ansi60_spls.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "ansi60_spls", 3 | 4 | "layout" : [ 5 | [ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8 ], 6 | [ 6, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6 ], 7 | [ 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 9 ], 8 | [ 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 11 ], 9 | [ 5, 5, 5, 25, 5, 5, 5, 5 ] 10 | ], 11 | 12 | "layers" : [ 13 | [ 14 | "ESC", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "+", "BACK", 15 | "TAB", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "[", "]", "|", 16 | "CAPS", "A", "S", "D", "F", "G", "H", "J", "K", "L", ";", "'", "ENTER", 17 | "SHIFT", "", "Z", "X", "C", "V", "B", "N", "M", ",", ".", "/", "SHIFT", 18 | "CTRL", "META", "ALT", "SPACE", "ALT", "FN", "MENU", "CTRL" 19 | ], 20 | [ 21 | "`", "", "", "", "", "", "", "", "", "", "", "", "", "", 22 | "", "", "", "", "", "", "", "", "UP", "", "", "", "", "", 23 | "", "", "", "", "", "", "", "LEFT", "DOWN", "RIGHT", "", "", "", 24 | "", "", "", "", "", "", "", "", "", "", "", "", "", 25 | "", "", "", "", "", "", "", "" 26 | ] 27 | ] 28 | } 29 | 30 | -------------------------------------------------------------------------------- /keymaps/ansi60_splsrs.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "ansi60_splsrs", 3 | 4 | "layout" : [ 5 | [ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8 ], 6 | [ 6, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6 ], 7 | [ 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 9 ], 8 | [ 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 7, 4 ], 9 | [ 5, 5, 5, 25, 5, 5, 5, 5 ] 10 | ], 11 | 12 | "layers" : [ 13 | [ 14 | "ESC", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "+", "BACK", 15 | "TAB", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "[", "]", "|", 16 | "CAPS", "A", "S", "D", "F", "G", "H", "J", "K", "L", ";", "'", "ENTER", 17 | "SHIFT", "", "Z", "X", "C", "V", "B", "N", "M", ",", ".", "/", "SHIFT", "", 18 | "CTRL", "META", "ALT", "SPACE", "ALT", "FN", "MENU", "CTRL" 19 | ], 20 | [ 21 | "`", "", "", "", "", "", "", "", "", "", "", "", "", "", 22 | "", "", "", "", "", "", "", "", "UP", "", "", "", "", "", 23 | "", "", "", "", "", "", "", "LEFT", "DOWN", "RIGHT", "", "", "", 24 | "", "", "", "", "", "", "", "", "", "", "", "", "", "", 25 | "", "", "", "", "", "", "", "" 26 | ] 27 | ] 28 | } 29 | 30 | -------------------------------------------------------------------------------- /keymaps/ansi80.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "ansi80", 3 | 4 | "layout" : [ 5 | [ 4, 132, 4, 4, 4, 4, 130, 4, 4, 4, 4, 130, 4, 4, 4, 4, 129, 4, 4, 4 ], 6 | [ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 129, 4, 4, 4 ], 7 | [ 6, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 129, 4, 4, 4 ], 8 | [ 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 9, 141 ], 9 | [ 9, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 11, 133, 4, 132 ], 10 | [ 5, 5, 5, 25, 5, 5, 5, 5, 129, 4, 4, 4 ] 11 | ], 12 | 13 | "layers" : [ 14 | [ 15 | "ESC", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "PRSC", "SCLK", "PAUS", 16 | "`", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "+", "BACK", "INS", "HOME", "PGUP", 17 | "TAB", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "[", "]", "|", "DEL", "END", "PGDN", 18 | "CAPS", "A", "S", "D", "F", "G", "H", "J", "K", "L", ";", "'", "ENTER", 19 | "SHIFT", "Z", "X", "C", "V", "B", "N", "M", ",", ".", "/", "SHIFT", "UP", 20 | "CTRL", "META", "ALT", "SPACE", "ALT", "FN", "MENU", "CTRL", "LEFT", "DOWN", "RIGHT" 21 | ] 22 | ] 23 | } 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2018, Charlie Waters 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /rawhid/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 2.8) 2 | PROJECT(rawhid) 3 | 4 | ### =================== SOURCES =================== ### 5 | 6 | SET(SOURCES 7 | hid.h 8 | hiddevice.h 9 | hiddevice.cpp 10 | ) 11 | 12 | SET(FILES 13 | hid_LINUX.c 14 | hid_MACOSX.c 15 | hid_WINDOWS.c 16 | ) 17 | 18 | IF(CMAKE_SYSTEM_NAME MATCHES "Windows") 19 | SET(SOURCES ${SOURCES} 20 | hid_WINDOWS.c 21 | ) 22 | ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Darwin") 23 | SET(SOURCES ${SOURCES} 24 | hid_MACOSX.c 25 | ) 26 | ELSE() 27 | SET(SOURCES ${SOURCES} 28 | hid_LINUX.c 29 | ) 30 | ENDIF() 31 | 32 | ### =================== CONFIG =================== ### 33 | 34 | IF(CMAKE_SYSTEM_NAME MATCHES "Darwin") 35 | # Find IOKit on Mac OS 36 | FIND_LIBRARY(IOKIT_LIBRARY IOKit REQUIRED) 37 | FIND_LIBRARY(COREFOUNDATION_LIBRARY CoreFoundation REQUIRED) 38 | ENDIF() 39 | 40 | ### =================== BUILD =================== ### 41 | 42 | ADD_CUSTOM_TARGET(rawhid-dummy SOURCES ${FILES}) 43 | 44 | ADD_LIBRARY(rawhid STATIC ${SOURCES}) 45 | SET_PROPERTY(TARGET rawhid PROPERTY CXX_STANDARD 11) 46 | TARGET_LINK_LIBRARIES(rawhid chaos-static) 47 | TARGET_COMPILE_OPTIONS(rawhid PRIVATE 48 | $<$: 49 | -std=gnu99 50 | > 51 | ) 52 | 53 | IF(CMAKE_SYSTEM_NAME MATCHES "Windows") 54 | # Windows 55 | TARGET_LINK_LIBRARIES(rawhid hid setupapi) 56 | ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Darwin") 57 | # Mac OS 58 | TARGET_LINK_LIBRARIES(rawhid ${IOKIT_LIBRARY} ${COREFOUNDATION_LIBRARY}) 59 | ELSE() 60 | # Linux/BSD 61 | TARGET_LINK_LIBRARIES(rawhid usb) 62 | ENDIF() 63 | 64 | # install 65 | install(TARGETS rawhid DESTINATION lib) 66 | -------------------------------------------------------------------------------- /kbscan.h: -------------------------------------------------------------------------------- 1 | #ifndef KBSCAN_H 2 | #define KBSCAN_H 3 | 4 | #include "kbproto.h" 5 | #include "rawhid/hiddevice.h" 6 | 7 | #include "zstring.h" 8 | #include "zpointer.h" 9 | using namespace LibChaos; 10 | 11 | enum DeviceType { 12 | DEV_NONE = 0, 13 | DEV_POK3R, //!< Vortex POK3R 14 | DEV_POK3R_RGB, //!< Vortex POK3R RGB 15 | DEV_POK3R_RGB2, //!< Vortex POK3R RGB (PCB v2) 16 | DEV_VORTEX_CORE, //!< Vortex Core 17 | DEV_VORTEX_RACE3, //!< Vortex Race 3 18 | // DEV_VORTEX_TESTER, //!< Vortex 22-Key Switch Tester (same as MD200) 19 | DEV_VORTEX_VIBE, //!< Vortex ViBE 20 | DEV_VORTEX_CYPHER, //!< Vortex Cypher 21 | DEV_VORTEX_TAB60, //!< Vortex Tab 60 22 | DEV_VORTEX_TAB75, //!< Vortex Tab 75 23 | DEV_VORTEX_TAB90, //!< Vortex Tab 90 24 | DEV_KBP_V60, //!< KBParadise v60 Mini 25 | DEV_KBP_V80, //!< KBParadise v80 26 | DEV_TEX_YODA_II, //!< Tex Yoda II 27 | DEV_MISTEL_MD600, //!< Mistel Barocco MD600 28 | DEV_MISTEL_MD200, //!< Mistel Freeboard MD200 29 | 30 | DEV_QMK_POK3R, 31 | DEV_QMK_POK3R_RGB, 32 | DEV_QMK_VORTEX_CORE, 33 | }; 34 | 35 | struct DeviceInfo { 36 | ZString slug; 37 | ZString name; 38 | zu16 vid; 39 | zu16 pid; 40 | zu16 boot_pid; 41 | KBType type; 42 | zu32 fw_addr; 43 | }; 44 | 45 | struct ListDevice { 46 | DeviceType devtype; 47 | DeviceInfo dev; 48 | ZPointer hid; 49 | bool boot; 50 | }; 51 | 52 | struct KBDevice { 53 | KBType type; 54 | DeviceType devtype; 55 | DeviceInfo info; 56 | ZPointer iface; 57 | }; 58 | 59 | class KBScan { 60 | public: 61 | KBScan(); 62 | 63 | zu32 find(DeviceType devtype); 64 | zu32 scan(); 65 | 66 | void dbgScan(); 67 | 68 | ZList open(); 69 | 70 | static ZPointer openConsole(DeviceType devtype); 71 | 72 | private: 73 | ZList devices; 74 | }; 75 | 76 | #endif // KBSCAN_H 77 | -------------------------------------------------------------------------------- /rawhid/hid.h: -------------------------------------------------------------------------------- 1 | #ifndef HID_H 2 | #define HID_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | typedef struct hid_struct hid_t; 9 | 10 | // some steps are skipped on some platforms because 11 | // there is no meaningful distinction between them 12 | 13 | // for each device before interfaces are enumerated 14 | #define RAWHID_STEP_DEV 1 15 | // for each interface before interface is detached/claimed 16 | #define RAWHID_STEP_IFACE 2 // not on windows or macosx 17 | // for each HID report descriptor before hid_t is created 18 | #define RAWHID_STEP_REPORT 3 // not on windows 19 | // for each opened hid_t 20 | #define RAWHID_STEP_OPEN 4 21 | 22 | struct rawhid_detail { 23 | int step; 24 | // device 25 | unsigned long bus; // not on windows or macosx 26 | unsigned long device; // not on windows or macosx 27 | unsigned short vid; 28 | unsigned short pid; 29 | // interface 30 | unsigned char ifnum; // not on windows or macosx 31 | unsigned char ifclass; // not on windows or macosx 32 | unsigned char subclass; // not on windows or macosx 33 | unsigned char protocol; // not on windows or macosx 34 | unsigned char ep_in; // not on windows or macosx 35 | unsigned char epin_size; // not on windows or macosx 36 | unsigned char ep_out; // not on windows or macosx 37 | unsigned char epout_size; // not on windows or macosx 38 | // report desc 39 | const unsigned char *report_desc; // not on windows 40 | unsigned short rdesc_len; // not on windows 41 | unsigned short usage_page; 42 | unsigned short usage; 43 | // open 44 | hid_t *hid; 45 | }; 46 | 47 | typedef int (*rawhid_filter_cb)(void *user, struct rawhid_detail *detail); 48 | 49 | hid_t *rawhid_open(int vid, int pid, int usage_page, int usage); 50 | void rawhid_close(hid_t *hid); 51 | 52 | int rawhid_openall(hid_t **hid, int max, int vid, int pid, int usage_page, int usage); 53 | int rawhid_openall_filter(rawhid_filter_cb cb, void *user); 54 | 55 | int rawhid_recv(hid_t *hid, void *buf, int len, int timeout); 56 | int rawhid_send(hid_t *hid, const void *buf, int len, int timeout); 57 | 58 | #ifdef __cplusplus 59 | } 60 | #endif 61 | 62 | #endif // HID_H 63 | -------------------------------------------------------------------------------- /kbproto.h: -------------------------------------------------------------------------------- 1 | #ifndef KBPROTO_H 2 | #define KBPROTO_H 3 | 4 | #include "zstring.h" 5 | #include "zbinary.h" 6 | using namespace LibChaos; 7 | 8 | #define UPDATE_USAGE_PAGE 0xff00 9 | #define UPDATE_USAGE 0x01 10 | 11 | enum KBType { 12 | PROTO_POK3R, //!< Used in the POK3R and KBP keyboards. 13 | PROTO_CYKB, //!< Used in Vortex keyboards marked with CYKB. 14 | }; 15 | 16 | enum KBStatus { 17 | ERR_FALSE = false, 18 | ERR_TRUE = true, 19 | SUCCESS = ERR_TRUE, 20 | ERR_NOT_IMPLEMENTED, //! Function not implemented. 21 | ERR_USAGE, //! Incorrect function usage. 22 | ERR_IO, //! I/O error. 23 | ERR_FLASH, //! Incorrect flash value. 24 | ERR_CRC, //! Incorrect CRC value. 25 | ERR_FAIL = ERR_FALSE, //! General failure (TODO where used). 26 | }; 27 | 28 | class KBProto { 29 | protected: 30 | KBProto(KBType type); 31 | public: 32 | virtual ~KBProto(){} 33 | 34 | //! Open device. 35 | virtual bool open() = 0; 36 | virtual void close() = 0; 37 | virtual bool isOpen() const = 0; 38 | 39 | virtual bool isBuiltin(){ return false; } 40 | virtual bool isQMK(){ return false; } 41 | 42 | //! Reset to firmware and re-open device if needed. 43 | virtual bool rebootFirmware(bool reopen = true) = 0; 44 | //! Reset to bootloader and re-open device if needed. 45 | virtual bool rebootBootloader(bool reopen = true) = 0; 46 | 47 | //! Read the firmware version from the device. 48 | virtual ZString getVersion() = 0; 49 | 50 | virtual KBStatus clearVersion(){ return ERR_NOT_IMPLEMENTED; } 51 | virtual KBStatus setVersion(ZString version){ return ERR_NOT_IMPLEMENTED; } 52 | 53 | virtual bool getInfo(){ return false; } 54 | 55 | //! Dump the contents of flash. 56 | virtual ZBinary dumpFlash(){ return ZBinary(); } 57 | 58 | //! Write the firmware. 59 | virtual bool writeFirmware(const ZBinary &fwbin) = 0; 60 | 61 | //! Complete update wrapper. 62 | virtual bool update(ZString version, const ZBinary &fwbin); 63 | 64 | virtual bool eraseAndCheck(){ return false; } 65 | 66 | KBType type() { return _type; } 67 | 68 | private: 69 | KBType _type; 70 | }; 71 | 72 | #endif // KBPROTO_H 73 | -------------------------------------------------------------------------------- /rawhid/rawhid_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #if defined(OS_LINUX) || defined(OS_MACOSX) 6 | #include 7 | #include 8 | #elif defined(OS_WINDOWS) 9 | #include 10 | #endif 11 | 12 | #include "hid.h" 13 | 14 | 15 | static char get_keystroke(void); 16 | 17 | 18 | int main() 19 | { 20 | int i, r, num; 21 | char c, buf[64]; 22 | 23 | // C-based example is 16C0:0480:FFAB:0200 24 | r = rawhid_open(1, 0x16C0, 0x0480, 0xFFAB, 0x0200); 25 | if (r <= 0) { 26 | // Arduino-based example is 16C0:0486:FFAB:0200 27 | r = rawhid_open(1, 0x16C0, 0x0486, 0xFFAB, 0x0200); 28 | if (r <= 0) { 29 | printf("no rawhid device found\n"); 30 | return -1; 31 | } 32 | } 33 | printf("found rawhid device\n"); 34 | 35 | while (1) { 36 | // check if any Raw HID packet has arrived 37 | num = rawhid_recv(0, buf, 64, 220); 38 | if (num < 0) { 39 | printf("\nerror reading, device went offline\n"); 40 | rawhid_close(0); 41 | return 0; 42 | } 43 | if (num > 0) { 44 | printf("\nrecv %d bytes:\n", num); 45 | for (i=0; i= 32) { 53 | printf("\ngot key '%c', sending...\n", c); 54 | buf[0] = c; 55 | for (i=1; i<64; i++) { 56 | buf[i] = 0; 57 | } 58 | rawhid_send(0, buf, 64, 100); 59 | } 60 | } 61 | } 62 | 63 | #if defined(OS_LINUX) || defined(OS_MACOSX) 64 | // Linux (POSIX) implementation of _kbhit(). 65 | // Morgan McGuire, morgan@cs.brown.edu 66 | static int _kbhit() { 67 | static const int STDIN = 0; 68 | static int initialized = 0; 69 | int bytesWaiting; 70 | 71 | if (!initialized) { 72 | // Use termios to turn off line buffering 73 | struct termios term; 74 | tcgetattr(STDIN, &term); 75 | term.c_lflag &= ~ICANON; 76 | tcsetattr(STDIN, TCSANOW, &term); 77 | setbuf(stdin, NULL); 78 | initialized = 1; 79 | } 80 | ioctl(STDIN, FIONREAD, &bytesWaiting); 81 | return bytesWaiting; 82 | } 83 | static char _getch(void) { 84 | char c; 85 | if (fread(&c, 1, 1, stdin) < 1) return 0; 86 | return c; 87 | } 88 | #endif 89 | 90 | 91 | static char get_keystroke(void) 92 | { 93 | if (_kbhit()) { 94 | char c = _getch(); 95 | if (c >= 32) return c; 96 | } 97 | return 0; 98 | } 99 | 100 | 101 | -------------------------------------------------------------------------------- /keymap.h: -------------------------------------------------------------------------------- 1 | #ifndef KEYMAP_H 2 | #define KEYMAP_H 3 | 4 | #include "zstring.h" 5 | #include "zbinary.h" 6 | #include "zmap.h" 7 | using namespace LibChaos; 8 | 9 | class Keymap { 10 | public: 11 | typedef zu16 keycode; 12 | struct Key { 13 | Key() : row(0xFF), col(0xFF), width(0), space(false), newrow(false), raw(0){} 14 | zu8 row, col; 15 | zu8 width; 16 | bool space; 17 | bool newrow; 18 | int raw; 19 | }; 20 | 21 | struct Keycode { 22 | Keymap::keycode keycode; 23 | ZString name; 24 | ZString abbrev; 25 | ZString desc; 26 | }; 27 | 28 | public: 29 | Keymap(zu8 rows, zu8 cols); 30 | 31 | void loadLayout(ZString name, ZBinary layout); 32 | void loadLayerMap(ZBinary layer); 33 | 34 | ZArray> getKeycodeLayout(zu8 layer) const; 35 | //! Get layer matrices binary for firmware. 36 | ZBinary toMatrix() const; 37 | 38 | //! Pretty-print layout layers and keycodes. 39 | void printLayers() const; 40 | //! Print layer matrices and keycodes. 41 | void printMatrix() const; 42 | 43 | keycode get(zu8 l, zu16 k) const { return layers[l][k]; } 44 | void set(zu8 l, zu16 k, keycode kc){ layers[l][k] = kc; } 45 | 46 | ZArray getLayer(zu8 layer) const; 47 | ZArray getLayerAbbrev(zu8 layer) const; 48 | 49 | //! Get number of keys in layout. 50 | zu16 numKeys() const { return nkeys; } 51 | //! Get number of layers. 52 | zsize numLayers() const { return layers.size(); } 53 | //! Get number of rows in layout. 54 | zu16 rowCount(zu8 row) const; 55 | 56 | //! Get name of current layout. 57 | ZString layoutName() const { return layout_name; } 58 | ZArray getLayout() const; 59 | //! Covert layout key row and column to key number. 60 | zu16 layoutRC2K(zu8 r, zu8 c) const; 61 | 62 | zu16 keyOffset(zu8 l, zu16 k) const; 63 | 64 | keycode toKeycode(ZString name) const; 65 | ZString keycodeName(keycode kc) const; 66 | ZString keycodeAbbrev(keycode kc) const; 67 | ZString keycodeDesc(keycode kc) const; 68 | 69 | static const ZArray &getAllKeycodes(); 70 | static ZArray getKnownLayouts(); 71 | 72 | private: 73 | //! Matrix rows, columns. 74 | const zu8 rows, cols; 75 | zu16 nkeys; 76 | ZMap lkmap; 77 | ZArray wlayout; 78 | zu16 mwidth; 79 | ZArray matrix2layout; 80 | ZArray layout2matrix; 81 | ZArray> layers; 82 | 83 | ZString layout_name; 84 | }; 85 | 86 | #endif // KEYMAP_H 87 | -------------------------------------------------------------------------------- /FindLibUSB.cmake: -------------------------------------------------------------------------------- 1 | # - Find libusb for portable USB support 2 | # 3 | # If the LibUSB_ROOT environment variable 4 | # is defined, it will be used as base path. 5 | # The following standard variables get defined: 6 | # LibUSB_FOUND: true if LibUSB was found 7 | # LibUSB_INCLUDE_DIR: the directory that contains the include file 8 | # LibUSB_LIBRARIES: the libraries 9 | 10 | IF(PKG_CONFIG_FOUND) 11 | IF(DEPENDS_DIR) #Otherwise use System pkg-config path 12 | SET(ENV{PKG_CONFIG_PATH} "${DEPENDS_DIR}/libusb/lib/pkgconfig") 13 | ENDIF() 14 | SET(MODULE "libusb-1.0") 15 | IF(CMAKE_SYSTEM_NAME MATCHES "Linux") 16 | SET(MODULE "libusb-1.0>=1.0.20") 17 | ENDIF() 18 | IF(LibUSB_FIND_REQUIRED) 19 | SET(LibUSB_REQUIRED "REQUIRED") 20 | ENDIF() 21 | PKG_CHECK_MODULES(LibUSB ${LibUSB_REQUIRED} ${MODULE}) 22 | 23 | FIND_LIBRARY(LibUSB_LIBRARY 24 | NAMES ${LibUSB_LIBRARIES} 25 | HINTS ${LibUSB_LIBRARY_DIRS} 26 | ) 27 | SET(LibUSB_LIBRARIES ${LibUSB_LIBRARY}) 28 | 29 | RETURN() 30 | ENDIF() 31 | 32 | FIND_PATH(LibUSB_INCLUDE_DIRS 33 | NAMES libusb.h 34 | PATHS 35 | "${DEPENDS_DIR}/libusb" 36 | "${DEPENDS_DIR}/libusbx" 37 | ENV LibUSB_ROOT 38 | PATH_SUFFIXES 39 | include 40 | libusb 41 | include/libusb-1.0 42 | ) 43 | 44 | SET(LIBUSB_NAME libusb) 45 | IF(WIN32) 46 | INCLUDE(CheckCSourceRuns) 47 | CHECK_C_SOURCE_RUNS("#include \nint main(){return !LoadLibraryA(\"libusbK\");}" LIBUSB_WITH_LIBUSBK) 48 | CHECK_C_SOURCE_RUNS("#include \nint main(){return !LoadLibraryA(\"UsbDkHelper\");}" LIBUSB_WITH_USBDK) 49 | 50 | IF(LIBUSB_USE_USBDK) 51 | SET(LIBUSB_NAME libusb-usbdk) 52 | ENDIF() 53 | 54 | IF(LIBUSB_NAME MATCHES ^libusb-usbdk$ AND NOT LIBUSB_WITH_USBDK) 55 | MESSAGE(WARNING "UsbDk device driver is not found. Fall back to libusbK.") 56 | SET(LIBUSB_NAME libusb) 57 | ENDIF() 58 | 59 | IF(LIBUSB_NAME MATCHES ^libusb$ AND NOT LIBUSB_WITH_LIBUSBK) 60 | MESSAGE(FATAL_ERROR "No USB device driver is installed.") 61 | ENDIF() 62 | ENDIF() 63 | 64 | FIND_LIBRARY(LibUSB_LIBRARIES 65 | NAMES ${LIBUSB_NAME}-1.0 66 | PATHS 67 | "${DEPENDS_DIR}/libusb" 68 | "${DEPENDS_DIR}/libusbx" 69 | ENV LibUSB_ROOT 70 | PATH_SUFFIXES 71 | x64/Release/dll 72 | x64/Debug/dll 73 | Win32/Release/dll 74 | Win32/Debug/dll 75 | MS64 76 | MS64/dll 77 | ) 78 | 79 | IF(WIN32) 80 | FIND_FILE(LibUSB_DLL 81 | ${LIBUSB_NAME}-1.0.dll 82 | PATHS 83 | "${DEPENDS_DIR}/libusb" 84 | "${DEPENDS_DIR}/libusbx" 85 | ENV LibUSB_ROOT 86 | PATH_SUFFIXES 87 | x64/Release/dll 88 | x64/Debug/dll 89 | Win32/Release/dll 90 | Win32/Debug/dll 91 | MS64 92 | MS64/dll 93 | ) 94 | IF(LibUSB_DLL AND LIBUSB_USE_USBDK) 95 | FILE(COPY ${LibUSB_DLL} DESTINATION ${CMAKE_BINARY_DIR}) 96 | SET(LibUSB_DLL ${CMAKE_BINARY_DIR}/libusb-1.0.dll) 97 | FILE(RENAME ${CMAKE_BINARY_DIR}/${LIBUSB_NAME}-1.0.dll ${LibUSB_DLL}) 98 | ENDIF() 99 | ENDIF() 100 | 101 | INCLUDE(FindPackageHandleStandardArgs) 102 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibUSB FOUND_VAR LibUSB_FOUND 103 | REQUIRED_VARS LibUSB_LIBRARIES LibUSB_INCLUDE_DIRS) 104 | 105 | -------------------------------------------------------------------------------- /proto_qmk.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTO_QMK_H 2 | #define PROTO_QMK_H 3 | 4 | #include "kbproto.h" 5 | #include "keymap.h" 6 | #include "rawhid/hiddevice.h" 7 | 8 | #include "zstring.h" 9 | #include "zbinary.h" 10 | using namespace LibChaos; 11 | 12 | #define QMK_EE_PAGE_SIZE 0x1000 13 | #define QMK_EE_CONF_PAGE 0x0 14 | #define QMK_EE_KEYM_PAGE 0x1000 15 | 16 | class ProtoQMK : public KBProto { 17 | public: 18 | enum qmk_cmd { 19 | CMD_CTRL = 0x81, //!< Control command. 20 | SUB_CT_INFO = 0, //!< Firmware info. 21 | SUB_CT_LAYOUT = 1, //!< Set layout. 22 | 23 | CMD_EEPROM = 0x82, //!< EEPROM commands. 24 | SUB_EE_INFO = 0, //!< EEPROM info (RDID SPI command). 25 | SUB_EE_READ = 1, //!< Read EEPROM data. 26 | SUB_EE_WRITE = 2, //!< Write EEPROM data. 27 | SUB_EE_ERASE = 3, //!< Erase EEPROM page. 28 | 29 | CMD_KEYMAP = 0x83, //!< Keymap commands. 30 | SUB_KM_INFO = 0, //!< Keymap info (layers, rows, cols, type size). 31 | SUB_KM_READ = 1, //!< Read keymap. 32 | KM_PAGE_MATRIX = 0, //!< Matrix page. 33 | KM_PAGE_LAYOUT = 1, //!< Layout page. 34 | KM_PAGE_STRS = 2, //!< Layout string page. 35 | SUB_KM_WRITE = 2, //!< Write to keymap. 36 | SUB_KM_COMMIT = 3, //!< Commit keymap to EEPROM. 37 | SUB_KM_RELOAD = 4, //!< Load keymap from EEPROM. 38 | SUB_KM_RESET = 5, //!< Load default keymap. 39 | 40 | CMD_BACKLIGHT = 0x84, //!< Backlight commands. 41 | SUB_BL_INFO = 0, //!< Backlight map info (layers, rows, cols, type size). 42 | SUB_BL_READ = 1, //!< Read backlight map. 43 | SUB_BL_WRITE = 2, //!< Write to backlight map. 44 | SUB_BL_COMMIT = 3, //!< Commit backlight map to EEPROM. 45 | 46 | CMD_FLASH_QMK = 0x85, //!< Flash commands. 47 | SUB_FL_READ = 0, //!< Read flash data. 48 | }; 49 | 50 | protected: 51 | ProtoQMK(KBType type, ZPointer dev) : KBProto(type), dev(dev){} 52 | public: 53 | virtual ~ProtoQMK(){} 54 | 55 | virtual bool isBuiltin() = 0; 56 | bool isQMK(); 57 | 58 | bool qmkInfo(); 59 | ZString qmkVersion(); 60 | 61 | bool eepromTest(); 62 | 63 | //! Dump the contents of external flash / eeprom. 64 | ZBinary dumpEEPROM(); 65 | //! Dump the keymap. 66 | bool keymapDump(); 67 | 68 | ZBinary getMatrix(); 69 | ZPointer loadKeymap(); 70 | bool uploadKeymap(ZPointer keymap); 71 | 72 | bool getKeymapInfo(ZBinary &info); 73 | 74 | bool getLayouts(ZArray &layouts); 75 | bool setLayout(zu8 layout); 76 | 77 | bool readEEPROM(zu32 addr, ZBinary &bin); 78 | bool writeEEPROM(zu32 addr, ZBinary bin); 79 | bool eraseEEPROM(zu32 addr); 80 | 81 | bool readKeymap(zu32 offset, ZBinary &bin); 82 | bool writeKeymap(zu16 offset, ZBinary bin); 83 | bool commitKeymap(); 84 | bool reloadKeymap(); 85 | bool resetKeymap(); 86 | 87 | protected: 88 | virtual zu32 baseFirmwareAddr() const = 0; 89 | 90 | private: 91 | bool sendRecvCmdQmk(zu8 cmd, zu8 subcmd, ZBinary &data, bool quiet = false); 92 | 93 | protected: 94 | ZPointer dev; 95 | ZBinary cachedMatrix; 96 | }; 97 | 98 | #endif // PROTO_QMK_H 99 | -------------------------------------------------------------------------------- /proto_cykb.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTO_CYKB_H 2 | #define PROTO_CYKB_H 3 | 4 | #include "kbproto.h" 5 | #include "proto_qmk.h" 6 | #include "rawhid/hiddevice.h" 7 | 8 | #include "zstring.h" 9 | #include "zbinary.h" 10 | using namespace LibChaos; 11 | 12 | class ProtoCYKB : public ProtoQMK { 13 | public: 14 | enum pok3r_rgb_cmd { 15 | CMD_16 = 0x10, 16 | CMD_16_ARG = 2, 17 | 18 | RESET = 0x11, //!< Reset command. 19 | RESET_BL = 0, //!< Reset to bootloader. 20 | RESET_FW = 1, //!< Reset to firmware. 21 | RESET_DIS = 2, //!< Disconnect USB. 22 | 23 | READ = 0x12, //!< Read command. 24 | READ_400 = 0, 25 | READ_3C00 = 1, 26 | READ_MODE = 2, //!< Get firmware mode. 0 is bootloader, 1 is firmware. 27 | READ_VER1 = 0x20, //!< Read version string. 28 | READ_VER2 = 0x22, //!< Read version data. 29 | READ_ADDR = 0xff, //!< Patched command, read arbitrary address. 30 | 31 | FW = 0x1d, //!< Firmware management command. 32 | FW_ERASE = 0, //!< Erase firmware. 33 | FW_SUM = 1, //!< Firwmare sum. 34 | FW_CRC = 2, //!< Firmware CRC. 35 | 36 | ADDR = 0x1e, //! Write address command. 37 | ADDR_GET = 0, //! Get write address. 38 | ADDR_SET = 1, //! Set write address. 39 | 40 | WRITE = 0x1f, //!< Write command. 41 | }; 42 | 43 | public: 44 | //! Construct unopened device. 45 | ProtoCYKB(zu16 vid, zu16 pid, zu16 boot_pid); 46 | //! Construct open device from open HIDDevice. 47 | ProtoCYKB(zu16 vid, zu16 pid, zu16 boot_pid, bool builtin, ZPointer dev, zu32 fw_addr); 48 | 49 | ProtoCYKB(const ProtoCYKB &) = delete; 50 | ~ProtoCYKB(); 51 | 52 | //! Find and open POK3R RGB device. 53 | bool open(); 54 | void close(); 55 | bool isOpen() const; 56 | 57 | bool isBuiltin(); 58 | 59 | //! Reset and re-open device. 60 | bool rebootFirmware(bool reopen = true); 61 | //! Reset to bootloader and re-open device. 62 | bool rebootBootloader(bool reopen = true); 63 | 64 | bool getInfo(); 65 | 66 | //! Read the firmware version from the keyboard. 67 | ZString getVersion(); 68 | 69 | KBStatus clearVersion(); 70 | KBStatus setVersion(ZString version); 71 | 72 | //! Dump the contents of flash. 73 | ZBinary dumpFlash(); 74 | //! Update the firmware. 75 | bool writeFirmware(const ZBinary &fwbin); 76 | 77 | bool eraseAndCheck(); 78 | 79 | void test(); 80 | 81 | //! Erase flash pages starting at \a start, ending on the page of \a end. 82 | bool eraseFlash(zu32 start, zu32 length); 83 | //! Read 64 bytes at \a addr. 84 | bool readFlash(zu32 addr, ZBinary &bin); 85 | //! Write 52 bytes at \a addr. 86 | bool writeFlash(zu32 addr, ZBinary bin); 87 | 88 | //! Get CRC of firmware. 89 | zu32 crcFlash(zu32 addr, zu32 len); 90 | 91 | private: 92 | zu32 baseFirmwareAddr() const; 93 | //! Send command 94 | bool sendCmd(zu8 cmd, zu8 a1, ZBinary data = ZBinary()); 95 | //! Recv command. 96 | bool recvCmd(ZBinary &data); 97 | //! Send command and recv response. 98 | bool sendRecvCmd(zu8 cmd, zu8 a1, ZBinary &data); 99 | 100 | public: 101 | static void decode_firmware(ZBinary &bin); 102 | static void encode_firmware(ZBinary &bin); 103 | 104 | static void info_section(ZBinary data); 105 | 106 | private: 107 | bool builtin; 108 | bool debug; 109 | bool nop; 110 | zu16 vid; 111 | zu16 pid; 112 | zu16 boot_pid; 113 | zu32 fw_addr; 114 | }; 115 | 116 | #endif // PROTO_CYKB_H 117 | -------------------------------------------------------------------------------- /rawhid/hiddevice.cpp: -------------------------------------------------------------------------------- 1 | #include "hiddevice.h" 2 | #include "zlog.h" 3 | #include "zmutex.h" 4 | #include "zlock.h" 5 | 6 | #include "hid.h" 7 | 8 | #if LIBCHAOS_PLATFORM == LIBCHAOS_PLATFORM_WINDOWS 9 | #include 10 | #elif LIBCHAOS_PLATFORM == LIBCHAOS_PLATFORM_LINUX 11 | #include 12 | #include 13 | #endif 14 | 15 | struct HIDDeviceData { 16 | hid_t *hid; 17 | }; 18 | 19 | HIDDevice::HIDDevice(){ 20 | hid = NULL; 21 | } 22 | 23 | HIDDevice::HIDDevice(hid_t *hidt){ 24 | hid = hidt; 25 | } 26 | 27 | HIDDevice::~HIDDevice(){ 28 | close(); 29 | } 30 | 31 | bool HIDDevice::open(zu16 vid, zu16 pid, zu16 usage_page, zu16 usage){ 32 | hid = rawhid_open(vid, pid, usage_page, usage); 33 | return (hid != NULL); 34 | } 35 | 36 | void HIDDevice::close(){ 37 | rawhid_close(hid); 38 | hid = NULL; 39 | } 40 | 41 | bool HIDDevice::isOpen() const { 42 | return !!(hid); 43 | } 44 | 45 | bool HIDDevice::send(const ZBinary &data, bool tolerate_dc){ 46 | if(!isOpen()) 47 | return false; 48 | int ret = rawhid_send(hid, data.raw(), data.size(), SEND_TIMEOUT); 49 | if(ret < 0){ 50 | #if LIBCHAOS_PLATFORM == LIBCHAOS_PLATFORM_WINDOWS 51 | zu32 err = ZError::getSystemErrorCode(); 52 | if(tolerate_dc && ( 53 | err == ERROR_GEN_FAILURE || 54 | err == ERROR_DEVICE_NOT_CONNECTED 55 | )){ 56 | // ignore some errors when devices may disconnect 57 | return true; 58 | } 59 | ELOG("hid send win32 error: " << err); 60 | #elif LIBCHAOS_PLATFORM == LIBCHAOS_PLATFORM_LINUX 61 | if(tolerate_dc && (ret == -EPIPE || ret == -ENXIO)){ 62 | // ignore some errors when devices may disconnect 63 | return true; 64 | } 65 | ELOG("hid send error: " << ret << ": " << usb_strerror()); 66 | #else 67 | ELOG("hid send error: " << ret); 68 | #endif 69 | return false; 70 | } 71 | if((zu64)ret != data.size()) 72 | return false; 73 | return true; 74 | } 75 | 76 | bool HIDDevice::recv(ZBinary &data){ 77 | if(!isOpen()) 78 | return false; 79 | if(data.size() == 0) 80 | return false; 81 | 82 | ZClock clock; 83 | int ret; 84 | do { 85 | ret = rawhid_recv(hid, data.raw(), data.size(), RECV_TIMEOUT); 86 | } while(ret == 0 && !clock.passedMs(RECV_TIMEOUT_MAX)); 87 | // } while(ret == 0); 88 | 89 | //if(ret == 0){ 90 | // ELOG("hid recv timeout"); 91 | // return false; 92 | //} else 93 | if(ret < 0){ 94 | #if LIBCHAOS_PLATFORM == LIBCHAOS_PLATFORM_LINUX 95 | ELOG("hid recv error: " << ret << ": " << usb_strerror()); 96 | #else 97 | ELOG("hid recv error: " << ret); 98 | #endif 99 | return false; 100 | } 101 | data.resize((zu64)ret); 102 | return true; 103 | } 104 | 105 | ZArray > HIDDevice::openAll(zu16 vid, zu16 pid, zu16 usage_page, zu16 usage){ 106 | ZArray> devs; 107 | #if LIBCHAOS_PLATFORM == LIBCHAOS_PLATFORM_MACOSX 108 | ELOG("HID openAll not supported on OSX yet"); 109 | #else 110 | hid_t *hid[128]; 111 | int count = rawhid_openall(hid, 128, vid, pid, usage_page, usage); 112 | 113 | for(int i = 0; i < count; ++i){ 114 | devs.push(new HIDDevice(hid[i])); 115 | } 116 | #endif 117 | return devs; 118 | } 119 | 120 | int open_cb(void *user, rawhid_detail *detail){ 121 | auto *func = (std::function *)user; 122 | return (int)(*func)(detail); 123 | } 124 | 125 | zu32 HIDDevice::openFilter(std::function func){ 126 | return rawhid_openall_filter(open_cb, (void *)&func); 127 | } 128 | -------------------------------------------------------------------------------- /proto_pok3r.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTO_POK3R_H 2 | #define PROTO_POK3R_H 3 | 4 | #include "kbproto.h" 5 | #include "proto_qmk.h" 6 | #include "rawhid/hiddevice.h" 7 | 8 | #include "zstring.h" 9 | #include "zbinary.h" 10 | using namespace LibChaos; 11 | 12 | class ProtoPOK3R : public ProtoQMK { 13 | public: 14 | enum pok3r_cmd { 15 | ERASE_CMD = 0, //! Erase pages of flash 16 | ERASE_NOP = 10, //! Cancel erase 17 | 18 | FLASH_CMD = 1, //! Read/write flash 19 | FLASH_CHECK_SUBCMD = 0, //! Compare bytes in flash with sent bytes 20 | FLASH_WRITE_SUBCMD = 1, //! Write 52 bytes to flash 21 | FLASH_READ_SUBCMD = 2, //! Read 64 bytes from flash 22 | FLASH_3_SUBCMD = 3, // ? 23 | 24 | CRC_CMD = 2, //! CRC part of flash 25 | 26 | UPDATE_START_CMD = 3, //! Start update 27 | 28 | RESET_CMD = 4, //! Reset processor 29 | RESET_BOOT_SUBCMD = 0, //! Reset to opposite firmware (main -> builtin, builtin -> main) 30 | RESET_BUILTIN_SUBCMD = 1, //! Reset to builtin firmware 31 | 32 | DISCONNECT_CMD = 5, //! Only in builtin firmware, disconnect USB and force reset 33 | 34 | DEBUG_CMD = 6, //! Collection of debug commands 35 | DEBUG_0_SUBCMD = 0, // ? 36 | DEBUG_1_SUBCMD = 1, // ? 37 | DEBUG_2_SUBCMD = 2, // ? 38 | DEBUG_3_SUBCMD = 3, // ? 39 | DEBUG_4_SUBCMD = 4, // ? 40 | DEBUG_5_SUBCMD = 5, // ? 41 | }; 42 | 43 | public: 44 | //! Construct unopened device. 45 | ProtoPOK3R(zu16 vid, zu16 pid, zu16 boot_pid); 46 | //! Construct open device from open HIDDevice. 47 | ProtoPOK3R(zu16 vid, zu16 pid, zu16 boot_pid, bool builtin, ZPointer dev); 48 | 49 | ProtoPOK3R(const ProtoPOK3R &) = delete; 50 | ~ProtoPOK3R(){} 51 | 52 | //! Find and open POK3R device. 53 | bool open(); 54 | void close(); 55 | bool isOpen() const; 56 | 57 | bool isBuiltin(); 58 | 59 | //! Reset and re-open device. 60 | bool rebootFirmware(bool reopen = true); 61 | //! Reset to loader and re-open device. 62 | bool rebootBootloader(bool reopen = true); 63 | 64 | bool getInfo(); 65 | 66 | //! Read the firmware version from the keyboard. 67 | ZString getVersion(); 68 | 69 | KBStatus clearVersion(); 70 | KBStatus setVersion(ZString version); 71 | 72 | //! Dump the contents of flash. 73 | ZBinary dumpFlash(); 74 | 75 | //! Update the firmware. 76 | bool writeFirmware(const ZBinary &fwbin); 77 | 78 | //! Read 64 bytes at \a addr. 79 | bool readFlash(zu32 addr, ZBinary &bin); 80 | //! Write 52 bytes at \a addr. 81 | bool writeFlash(zu32 addr, ZBinary bin); 82 | //! Check 52 bytes at \a addr. 83 | bool checkFlash(zu32 addr, ZBinary bin); 84 | //! Erase flash pages starting at \a start, ending on the page of \a end. 85 | bool eraseFlash(zu32 start, zu32 end); 86 | 87 | //! Send CRC command. 88 | zu16 crcFlash(zu32 addr, zu32 len); 89 | 90 | private: 91 | zu32 baseFirmwareAddr() const; 92 | //! Send command 93 | bool sendCmd(zu8 cmd, zu8 subcmd, ZBinary bin = ZBinary()); 94 | //! Send command and recv response. 95 | bool sendRecvCmd(zu8 cmd, zu8 subcmd, ZBinary &data); 96 | 97 | public: 98 | static void decode_firmware(ZBinary &bin); 99 | static void encode_firmware(ZBinary &bin); 100 | 101 | private: 102 | bool builtin; 103 | bool debug; 104 | bool nop; 105 | zu16 vid; 106 | zu16 pid; 107 | zu16 boot_pid; 108 | }; 109 | 110 | #endif // PROTO_POK3R_H 111 | -------------------------------------------------------------------------------- /FindLibUSB-1.0.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find libusb-1.0 2 | # Once done this will define 3 | # 4 | # LIBUSB_1_FOUND - system has libusb 5 | # LIBUSB_1_INCLUDE_DIRS - the libusb include directory 6 | # LIBUSB_1_LIBRARIES - Link these to use libusb 7 | # LIBUSB_1_DEFINITIONS - Compiler switches required for using libusb 8 | # 9 | # Adapted from cmake-modules Google Code project 10 | # 11 | # Copyright (c) 2006 Andreas Schneider 12 | # 13 | # (Changes for libusb) Copyright (c) 2008 Kyle Machulis 14 | # 15 | # Redistribution and use is allowed according to the terms of the New BSD license. 16 | # 17 | # CMake-Modules Project New BSD License 18 | # 19 | # Redistribution and use in source and binary forms, with or without 20 | # modification, are permitted provided that the following conditions are met: 21 | # 22 | # * Redistributions of source code must retain the above copyright notice, this 23 | # list of conditions and the following disclaimer. 24 | # 25 | # * Redistributions in binary form must reproduce the above copyright notice, 26 | # this list of conditions and the following disclaimer in the 27 | # documentation and/or other materials provided with the distribution. 28 | # 29 | # * Neither the name of the CMake-Modules Project nor the names of its 30 | # contributors may be used to endorse or promote products derived from this 31 | # software without specific prior written permission. 32 | # 33 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 34 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 35 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 36 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 37 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 38 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 39 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 40 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 41 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 42 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 43 | # 44 | 45 | 46 | if (LIBUSB_1_LIBRARIES AND LIBUSB_1_INCLUDE_DIRS) 47 | # in cache already 48 | set(LIBUSB_FOUND TRUE) 49 | else (LIBUSB_1_LIBRARIES AND LIBUSB_1_INCLUDE_DIRS) 50 | find_path(LIBUSB_1_INCLUDE_DIR 51 | NAMES 52 | libusb.h 53 | PATHS 54 | /usr/include 55 | /usr/local/include 56 | /opt/local/include 57 | /sw/include 58 | ${LIBUSB_HINT_INCLUDE_DIR} 59 | PATH_SUFFIXES 60 | libusb-1.0 61 | ) 62 | 63 | find_library(LIBUSB_1_LIBRARY 64 | NAMES 65 | libusb-1.0.a 66 | usb-1.0 67 | usb 68 | PATHS 69 | /usr/lib 70 | /usr/local/lib 71 | /opt/local/lib 72 | /sw/lib 73 | ${LIBUSB_HINT_LIBRARY_DIR} 74 | ) 75 | 76 | set(LIBUSB_1_INCLUDE_DIRS 77 | ${LIBUSB_1_INCLUDE_DIR} 78 | ) 79 | set(LIBUSB_1_LIBRARIES 80 | ${LIBUSB_1_LIBRARY} 81 | ) 82 | 83 | if (LIBUSB_1_INCLUDE_DIRS AND LIBUSB_1_LIBRARIES) 84 | set(LIBUSB_1_FOUND TRUE) 85 | endif (LIBUSB_1_INCLUDE_DIRS AND LIBUSB_1_LIBRARIES) 86 | 87 | if (LIBUSB_1_FOUND) 88 | if (NOT libusb_1_FIND_QUIETLY) 89 | message(STATUS "Found libusb-1.0:") 90 | message(STATUS " - Includes: ${LIBUSB_1_INCLUDE_DIRS}") 91 | message(STATUS " - Libraries: ${LIBUSB_1_LIBRARIES}") 92 | endif (NOT libusb_1_FIND_QUIETLY) 93 | else (LIBUSB_1_FOUND) 94 | if (libusb_1_FIND_REQUIRED) 95 | message(FATAL_ERROR "Could not find libusb") 96 | endif (libusb_1_FIND_REQUIRED) 97 | endif (LIBUSB_1_FOUND) 98 | 99 | # show the LIBUSB_1_INCLUDE_DIRS and LIBUSB_1_LIBRARIES variables only in the advanced view 100 | mark_as_advanced(LIBUSB_1_INCLUDE_DIRS LIBUSB_1_LIBRARIES) 101 | 102 | endif (LIBUSB_1_LIBRARIES AND LIBUSB_1_INCLUDE_DIRS) 103 | 104 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ## Pok3rTool CMakeLists.txt 2 | cmake_minimum_required(VERSION 3.0) 3 | 4 | project(Pok3rTool) 5 | 6 | add_subdirectory(libchaos) 7 | add_subdirectory(rawhid) 8 | 9 | set(JSON_BuildTests OFF CACHE BOOL "disable json tests") 10 | set(JSON_MultipleHeaders ON CACHE BOOL "enable json multiple headers") 11 | add_subdirectory(nlohmann_json) 12 | 13 | ### =================== SOURCES =================== ### 14 | 15 | set(GEN_KEYMAPS_HEADER ${CMAKE_CURRENT_BINARY_DIR}/gen_keymaps.h) 16 | 17 | set(POK3RLIB_SOURCES 18 | kbscan.h 19 | kbscan.cpp 20 | 21 | kbproto.h 22 | kbproto.cpp 23 | proto_pok3r.h 24 | proto_pok3r.cpp 25 | proto_cykb.h 26 | proto_cykb.cpp 27 | proto_qmk.h 28 | proto_qmk.cpp 29 | 30 | keycodes.h 31 | keymap.h 32 | keymap.cpp 33 | 34 | ${GEN_KEYMAPS_HEADER} 35 | ) 36 | 37 | set(POK3RTOOL_SOURCES 38 | main.cpp 39 | 40 | updatepackage.h 41 | updatepackage.cpp 42 | 43 | ${GEN_KEYMAPS_HEADER} 44 | ) 45 | 46 | set(FILES 47 | README.md 48 | 99-pok3r.rules 49 | test.sh 50 | ) 51 | 52 | ### =================== BUILD =================== ### 53 | 54 | #add_custom_target(pok3rtool-dummy SOURCES ${FILES}) 55 | 56 | # Pok3rLib 57 | add_library(pok3rlib STATIC ${POK3RLIB_SOURCES}) 58 | set_property(TARGET pok3rlib PROPERTY CXX_STANDARD 11) 59 | target_link_libraries(pok3rlib chaos-static rawhid nlohmann_json ${LIBUSB_1_LIBRARIES}) 60 | #set_property(TARGET chaos-static PROPERTY POSITION_INDEPENDENT_CODE ON) 61 | target_include_directories(pok3rlib PRIVATE ${LIBUSB_1_INCLUDE_DIRECTORIES} ${CMAKE_CURRENT_BINARY_DIR}) 62 | target_compile_definitions(pok3rlib PRIVATE ${LIBUSB_1_DEFINITIONS}) 63 | 64 | # Pok3rTool 65 | add_executable(pok3rtool ${POK3RTOOL_SOURCES}) 66 | set_property(TARGET pok3rtool PROPERTY CXX_STANDARD 11) 67 | target_link_libraries(pok3rtool pok3rlib) 68 | target_include_directories(pok3rtool PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) 69 | 70 | if(WIN32) 71 | add_custom_command(TARGET pok3rtool POST_BUILD 72 | COMMAND ${CMAKE_COMMAND} -E copy_if_different 73 | "$" 74 | "$" 75 | COMMAND ${CMAKE_COMMAND} -E copy_if_different 76 | "$" 77 | "$" 78 | ) 79 | endif() 80 | 81 | # install tool and lib 82 | install(TARGETS pok3rtool DESTINATION bin) 83 | install(TARGETS pok3rlib DESTINATION lib) 84 | 85 | if(MINGW) 86 | # Apparently Qt's mingw doesn't come with some static libraries 87 | get_filename_component(MINGW_DIR ${CMAKE_CXX_COMPILER} DIRECTORY) 88 | set(MINGW_LIBS_DIR "${MINGW_DIR}/../x86_64-w64-mingw32/lib") 89 | set(MINGW_LIBGCC "${MINGW_LIBS_DIR}/libgcc_s_seh-1.dll") 90 | set(MINGW_LIBSTD "${MINGW_LIBS_DIR}/libstdc++-6.dll") 91 | set(MINGW_LIBWPT "${MINGW_LIBS_DIR}/libwinpthread-1.dll") 92 | 93 | add_custom_command(TARGET pok3rtool POST_BUILD 94 | COMMAND ${CMAKE_COMMAND} -E copy_if_different 95 | "${MINGW_LIBGCC}" 96 | "$" 97 | COMMAND ${CMAKE_COMMAND} -E copy_if_different 98 | "${MINGW_LIBSTD}" 99 | "$" 100 | COMMAND ${CMAKE_COMMAND} -E copy_if_different 101 | "${MINGW_LIBWPT}" 102 | "$" 103 | ) 104 | 105 | # intall mingw libraries 106 | install(FILES "${MINGW_LIBGCC}" DESTINATION lib) 107 | install(FILES "${MINGW_LIBSTD}" DESTINATION lib) 108 | install(FILES "${MINGW_LIBWPT}" DESTINATION lib) 109 | endif() 110 | 111 | # Creates C resources file from files in given directory 112 | function(create_resources dir output name) 113 | # Create empty output file 114 | file(WRITE ${output} "") 115 | # Collect input files 116 | file(GLOB bins ${dir}/*) 117 | 118 | # Iterate through input files 119 | foreach(bin ${bins}) 120 | # Get short filename 121 | string(REGEX MATCH "([^/]+)$" filename ${bin}) 122 | # Replace filename spaces & extension separator for C compatibility 123 | string(REGEX REPLACE "\\.| |-" "_" filename ${filename}) 124 | # Read hex data from file 125 | file(READ ${bin} filedata HEX) 126 | # Convert hex data for C compatibility 127 | string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," filedata ${filedata}) 128 | # Append data to output file 129 | file(APPEND ${output} "static const unsigned char ${filename}[] = {${filedata}};\nstatic const unsigned ${filename}_size = sizeof(${filename});\n") 130 | set(filenames "${filenames}${filename},") 131 | set(filename_sizes "${filename_sizes}${filename}_size,") 132 | endforeach() 133 | 134 | file(APPEND ${output} "static const unsigned char *${name}[] = {${filenames}};\nstatic const unsigned ${name}_sizes[] = {${filename_sizes}};\nstatic const unsigned ${name}_size = sizeof(${name}_sizes);") 135 | endfunction() 136 | 137 | create_resources("keymaps" "${GEN_KEYMAPS_HEADER}" "keymaps_json") 138 | -------------------------------------------------------------------------------- /proto_qmk.cpp: -------------------------------------------------------------------------------- 1 | #include "proto_qmk.h" 2 | #include "keymap.h" 3 | #include "keycodes.h" 4 | #include "zlog.h" 5 | 6 | #define UPDATE_PKT_LEN 64 7 | #define UPDATE_ERROR 0xaaff 8 | 9 | #define QMKID_OFFSET 0x160 10 | #define EEPROM_LEN 0x80000 11 | #define KM_READ_LAYOUT 0x10000 12 | #define KM_READ_LSTRS 0x20000 13 | 14 | #define HEX(A) (ZString::ItoS((zu64)(A), 16)) 15 | 16 | bool ProtoQMK::isQMK() { 17 | DLOG("isQMK"); 18 | if(isBuiltin()) 19 | return false; 20 | 21 | //dev->setStream(true); 22 | ZBinary data; 23 | if(!sendRecvCmdQmk(CMD_CTRL, SUB_CT_INFO, data, true)){ 24 | //dev->setStream(false); 25 | return false; 26 | } 27 | 28 | ArZ info = ZString(data.raw() + 4, 56).explode(';'); 29 | if(info.size() < 1 || info[0] != "qmk_pok3r"){ 30 | //dev->setStream(false); 31 | return false; 32 | } 33 | 34 | zu16 pid = data.readleu16(); 35 | zu16 ver = data.readleu16(); 36 | DLOG("qmk info " << HEX(pid) << " " << ver); 37 | return true; 38 | } 39 | 40 | bool ProtoQMK::qmkInfo(){ 41 | ZBinary data; 42 | if(!sendRecvCmdQmk(CMD_CTRL, SUB_CT_INFO, data, true)){ 43 | return false; 44 | } 45 | //LOG(ZLog::RAW << data.dumpBytes(4, 8)); 46 | 47 | zu16 pid = data.readleu16(); 48 | zu16 ver = data.readleu16(); 49 | LOG("pid: " << HEX(pid) << ", ver: " << ver); 50 | 51 | ArZ info = ZString(data.raw() + 4, 56).explode(';'); 52 | for(zsize i = 0; i < info.size(); ++i){ 53 | LOG(i << ": " << info[i]); 54 | } 55 | return true; 56 | } 57 | 58 | ZString ProtoQMK::qmkVersion(){ 59 | ZBinary data; 60 | if(!sendRecvCmdQmk(CMD_CTRL, SUB_CT_INFO, data, true)){ 61 | return false; 62 | } 63 | 64 | ArZ info = ZString(data.raw() + 4, 56).explode(';'); 65 | if(info.size() < 2){ 66 | return "ERROR"; 67 | } else { 68 | return info[1]; 69 | } 70 | } 71 | 72 | bool ProtoQMK::eepromTest(){ 73 | // Send command 74 | ZBinary data; 75 | if(!sendRecvCmdQmk(CMD_EEPROM, SUB_EE_INFO, data)) 76 | return false; 77 | 78 | LOG(ZLog::RAW << data.dumpBytes(4, 8)); 79 | 80 | ZBinary test; 81 | test.fill(0xaa, 56); 82 | writeEEPROM(0x1000, test); 83 | 84 | //eraseEEPROM(0x0000); 85 | 86 | return true; 87 | } 88 | 89 | ZBinary ProtoQMK::dumpEEPROM(){ 90 | ZBinary dump; 91 | zu32 cp = EEPROM_LEN / 10; 92 | int perc = 0; 93 | RLOG(perc << "%..."); 94 | for(zu32 addr = 0; addr < EEPROM_LEN; addr += 60){ 95 | if(!readEEPROM(addr, dump)) 96 | break; 97 | 98 | if(addr >= cp){ 99 | perc += 10; 100 | RLOG(perc << "%..."); 101 | cp += EEPROM_LEN / 10; 102 | } 103 | } 104 | RLOG("100%" << ZLog::NEWLN); 105 | 106 | return dump; 107 | } 108 | 109 | bool ProtoQMK::keymapDump(){ 110 | auto keymap = loadKeymap(); 111 | if(!keymap.get()){ 112 | ELOG("Unable to load keymap"); 113 | return false; 114 | } 115 | 116 | LOG("Keymap Dump: " << keymap->numKeys() << " keys, " << keymap->numLayers() << " layers"); 117 | LOG("Current Layout: " << keymap->layoutName()); 118 | 119 | keymap->printLayers(); 120 | //keymap->printMatrix(); 121 | 122 | return true; 123 | } 124 | 125 | ZBinary ProtoQMK::getMatrix(){ 126 | DLOG("loadKeymap"); 127 | // Send command 128 | ZBinary data; 129 | if(!sendRecvCmdQmk(CMD_KEYMAP, SUB_KM_INFO, data)) 130 | return nullptr; 131 | 132 | const zu8 layers = data[0]; 133 | const zu8 rows = data[1]; 134 | const zu8 cols = data[2]; 135 | const zu8 kcsize = data[3]; 136 | const zu8 nlayout = data[4]; 137 | const zu8 clayout = data[5]; 138 | 139 | const zu16 kmsize = kcsize * rows * cols; 140 | 141 | ZBinary matrices; 142 | for(int l = 0; l < layers; ++l){ 143 | ZBinary dump; 144 | for(zu32 off = 0; off < kmsize; off += 60){ 145 | if(!readKeymap((kmsize * l) + off, dump)) 146 | break; 147 | } 148 | dump.resize(kmsize); 149 | matrices.write(dump); 150 | } 151 | cachedMatrix = matrices; 152 | 153 | return matrices; 154 | } 155 | 156 | ZPointer ProtoQMK::loadKeymap(){ 157 | DLOG("loadKeymap"); 158 | ZBinary data; 159 | getKeymapInfo(data); 160 | 161 | const zu8 layers = data[0]; 162 | const zu8 rows = data[1]; 163 | const zu8 cols = data[2]; 164 | const zu8 kcsize = data[3]; 165 | const zu8 nlayout = data[4]; 166 | zu8 clayout = data[5]; 167 | 168 | const zu16 ksize = rows * cols; 169 | const zu16 kmsize = kcsize * rows * cols; 170 | 171 | ZArray lstrs; 172 | getLayouts(lstrs); 173 | zassert(lstrs.size() == nlayout, "Layout string count does not match num layouts"); 174 | 175 | // Read layouts 176 | ZArray layouts; 177 | for(int l = 0; l < nlayout; ++l){ 178 | ZBinary dump; 179 | for(zu32 off = 0; off < ksize; off += 60){ 180 | if(!readKeymap(KM_READ_LAYOUT + (ksize * l) + off, dump)) 181 | return nullptr; 182 | } 183 | dump.resize(ksize); 184 | layouts.push(dump); 185 | } 186 | 187 | zassert(clayout < layouts.size(), "Invalid current layout"); 188 | 189 | ZPointer keymap = new Keymap(rows, cols); 190 | keymap->loadLayout(lstrs[clayout], layouts[clayout]); 191 | 192 | // Read each layer into keymap 193 | ZBinary matrices; 194 | for(int l = 0; l < layers; ++l){ 195 | ZBinary dump; 196 | for(zu32 off = 0; off < kmsize; off += 60){ 197 | if(!readKeymap((kmsize * l) + off, dump)) 198 | return nullptr; 199 | } 200 | dump.resize(kmsize); 201 | matrices.write(dump); 202 | keymap->loadLayerMap(dump); 203 | } 204 | cachedMatrix = matrices; 205 | 206 | return keymap; 207 | } 208 | 209 | bool ProtoQMK::uploadKeymap(ZPointer keymap){ 210 | DLOG("uploadKeymap"); 211 | 212 | ZBinary dump = cachedMatrix; 213 | // use cached matrix if possible 214 | if(!dump.size()) 215 | dump = getMatrix(); 216 | //RLOG(dump.dumpBytes(2, 16)); 217 | 218 | ZBinary map = keymap->toMatrix(); 219 | //LOG("Writing " << map.size() << " matrix bytes"); 220 | //RLOG(map.dumpBytes(2, 16)); 221 | 222 | ZBinary diff; 223 | zu64 offset = dump.subDiff(map, diff); 224 | zassert(offset != ZU64_MAX, "invalid diff"); 225 | LOG("Keymap Diff:"); 226 | RLOG(diff.dumpBytes(2, 16, offset)); 227 | 228 | for(zu32 off = 0; off < diff.size(); off += 56){ 229 | ZBinary bin; 230 | diff.read(bin, 56); 231 | //RLOG(bin.dumpBytes(2, 16)); 232 | if(!writeKeymap(offset + off, bin)) 233 | return false; 234 | } 235 | 236 | // update cached matrix 237 | cachedMatrix = map; 238 | return true; 239 | } 240 | 241 | bool ProtoQMK::getKeymapInfo(ZBinary &info){ 242 | if(!sendRecvCmdQmk(CMD_KEYMAP, SUB_KM_INFO, info)) 243 | return false; 244 | return true; 245 | } 246 | 247 | bool ProtoQMK::getLayouts(ZArray &layouts){ 248 | // Read layout strs 249 | ZBinary lstr; 250 | for(zu32 off = 0; true; off += 60){ 251 | ZBinary dump; 252 | if(!readKeymap(KM_READ_LSTRS + off, dump)) 253 | return false; 254 | lstr.write(dump); 255 | 256 | bool nullt = false; 257 | for(zu64 i = 0; i < dump.size(); ++i){ 258 | if(dump[i] == 0){ 259 | nullt = true; 260 | break; 261 | } 262 | } 263 | if(nullt) 264 | break; 265 | } 266 | lstr.nullTerm(); 267 | layouts = ZString(lstr.asChar()).explode(','); 268 | return true; 269 | } 270 | 271 | bool ProtoQMK::setLayout(zu8 layout){ 272 | ZBinary data; 273 | data.writeu8(layout); 274 | if(!sendRecvCmdQmk(CMD_CTRL, SUB_CT_LAYOUT, data)) 275 | return false; 276 | return true; 277 | } 278 | 279 | bool ProtoQMK::readEEPROM(zu32 addr, ZBinary &bin){ 280 | DLOG("readEEPROM " << HEX(addr)); 281 | // Send command 282 | ZBinary data; 283 | data.writeleu32(addr); 284 | if(!sendRecvCmdQmk(CMD_EEPROM, SUB_EE_READ, data)) 285 | return false; 286 | bin.write(data); 287 | return true; 288 | } 289 | 290 | bool ProtoQMK::writeEEPROM(zu32 addr, ZBinary bin){ 291 | DLOG("writeEEPROM " << HEX(addr)); 292 | // Send command 293 | ZBinary data; 294 | data.writeleu32(addr); 295 | data.write(bin); 296 | if(!sendRecvCmdQmk(CMD_EEPROM, SUB_EE_WRITE, data)) 297 | return false; 298 | return true; 299 | } 300 | 301 | bool ProtoQMK::eraseEEPROM(zu32 addr){ 302 | DLOG("eraseEEPROM " << HEX(addr)); 303 | // Send command 304 | ZBinary data; 305 | data.writeleu32(addr); 306 | if(!sendRecvCmdQmk(CMD_EEPROM, SUB_EE_ERASE, data)) 307 | return false; 308 | return true; 309 | } 310 | 311 | bool ProtoQMK::readKeymap(zu32 offset, ZBinary &bin){ 312 | DLOG("readKeymap " << HEX(offset)); 313 | // Send command 314 | ZBinary data; 315 | data.writeleu32(offset); 316 | if(!sendRecvCmdQmk(CMD_KEYMAP, SUB_KM_READ, data)) 317 | return false; 318 | bin.write(data); 319 | return true; 320 | } 321 | 322 | bool ProtoQMK::writeKeymap(zu16 offset, ZBinary bin){ 323 | if(bin.size() > 56){ 324 | ELOG("keymap write too large"); 325 | return false; 326 | } 327 | DLOG("writeKeymap " << offset << " " << bin.size()); 328 | // Send command 329 | ZBinary data; 330 | data.writeleu16(offset); 331 | data.writeleu16(bin.size()); 332 | data.write(bin); 333 | if(!sendRecvCmdQmk(CMD_KEYMAP, SUB_KM_WRITE, data)) 334 | return false; 335 | return true; 336 | } 337 | 338 | bool ProtoQMK::commitKeymap(){ 339 | ZBinary data; 340 | if(!sendRecvCmdQmk(CMD_KEYMAP, SUB_KM_COMMIT, data)) 341 | return false; 342 | return true; 343 | } 344 | 345 | bool ProtoQMK::reloadKeymap(){ 346 | ZBinary data; 347 | if(!sendRecvCmdQmk(CMD_KEYMAP, SUB_KM_RELOAD, data)) 348 | return false; 349 | return true; 350 | } 351 | 352 | bool ProtoQMK::resetKeymap(){ 353 | ZBinary data; 354 | if(!sendRecvCmdQmk(CMD_KEYMAP, SUB_KM_RESET, data)) 355 | return false; 356 | return true; 357 | } 358 | 359 | bool ProtoQMK::sendRecvCmdQmk(zu8 cmd, zu8 subcmd, ZBinary &data, bool quiet){ 360 | if(data.size() > 60){ 361 | ELOG("bad data size"); 362 | return false; 363 | } 364 | 365 | // discard any unread data 366 | ZBinary tmp_buff; 367 | while(dev->recv(tmp_buff)){ 368 | DLOG("discard recv"); 369 | DLOG(ZLog::RAW << tmp_buff.dumpBytes(4, 8)); 370 | } 371 | 372 | ZBinary pkt_out(UPDATE_PKT_LEN); 373 | pkt_out.fill(0); 374 | pkt_out.writeu8(cmd); // command 375 | pkt_out.writeu8(subcmd); // subcommand 376 | pkt_out.seek(4); 377 | pkt_out.write(data); // data 378 | 379 | pkt_out.seek(2); 380 | zu16 crc_out = ZHash(pkt_out).hash(); 381 | pkt_out.writeleu16(crc_out); // CRC 382 | 383 | DLOG("send:"); 384 | DLOG(ZLog::RAW << pkt_out.dumpBytes(4, 8)); 385 | 386 | // Send command (interrupt write) 387 | if(!dev->send(pkt_out)){ 388 | ELOG("send error"); 389 | return false; 390 | } 391 | 392 | // Recv packet 393 | ZBinary pkt_in; 394 | pkt_in.resize(UPDATE_PKT_LEN); 395 | if(!dev->recv(pkt_in)){ 396 | ELOG("recv error"); 397 | return false; 398 | } 399 | 400 | DLOG("recv:"); 401 | DLOG(ZLog::RAW << pkt_in.dumpBytes(4, 8)); 402 | 403 | if(pkt_in.size() != UPDATE_PKT_LEN){ 404 | DLOG("bad recv size"); 405 | return false; 406 | } 407 | 408 | pkt_in.seek(4); 409 | pkt_in.read(data, 60); // read data 410 | data.rewind(); 411 | pkt_in.rewind(); 412 | zu16 crc0 = pkt_in.readleu16(); // crc for request 413 | zu16 crc1 = pkt_in.readleu16(); // crc for response 414 | pkt_in.seek(2); 415 | pkt_in.writeleu16(0); 416 | zu16 crc_in = ZHash(pkt_in).hash(); 417 | 418 | // check for error 419 | if(crc0 == UPDATE_ERROR && crc1 == 0){ 420 | if(quiet) 421 | return true; 422 | ELOG("command error"); 423 | return false; 424 | } 425 | 426 | // compare response crc 427 | if(crc1 != crc_in){ 428 | ELOG("response invalid crc " << HEX(crc1) << " expected " << HEX(crc_in)); 429 | return false; 430 | } 431 | 432 | // compare request crc 433 | if(crc_out != crc0){ 434 | ELOG("response out of sequence" << HEX(crc0) << " expected " << HEX(crc_out)); 435 | return false; 436 | } 437 | 438 | return true; 439 | } 440 | -------------------------------------------------------------------------------- /proto_pok3r.cpp: -------------------------------------------------------------------------------- 1 | #include "proto_pok3r.h" 2 | #include "keycodes.h" 3 | #include "zlog.h" 4 | 5 | #define UPDATE_PKT_LEN 64 6 | 7 | #define VER_ADDR 0x2800 8 | #define FW_ADDR 0x2c00 9 | 10 | #define FLASH_LEN 0x20000 11 | #define EEPROM_LEN 0x80000 12 | 13 | #define REBOOT_SLEEP 5 14 | #define ERASE_SLEEP 2 15 | 16 | #define HEX(A) (ZString::ItoS((zu64)(A), 16)) 17 | 18 | ProtoPOK3R::ProtoPOK3R(zu16 vid_, zu16 pid_, zu16 boot_pid_) : 19 | ProtoQMK(PROTO_POK3R, new HIDDevice), 20 | builtin(false), debug(false), nop(false), 21 | vid(vid_), pid(pid_), boot_pid(boot_pid_) 22 | { 23 | 24 | } 25 | 26 | ProtoPOK3R::ProtoPOK3R(zu16 vid_, zu16 pid_, zu16 boot_pid_, bool builtin_, ZPointer dev_) : 27 | ProtoQMK(PROTO_POK3R, dev_), 28 | builtin(builtin_), debug(false), nop(false), 29 | vid(vid_), pid(pid_), boot_pid(boot_pid_) 30 | { 31 | /* 32 | if(dev.get() && dev.get()->isOpen()){ 33 | if(!sendCmd(QMK_INFO, 0, 0, 0)) 34 | return; 35 | 36 | ZBinary data(64); 37 | if(!dev->recv(data)){ 38 | ELOG("recv error"); 39 | return; 40 | } 41 | DLOG("recv:"); 42 | LOG(ZLog::RAW << data.dumpBytes(4, 8)); 43 | } 44 | */ 45 | 46 | } 47 | 48 | bool ProtoPOK3R::open(){ 49 | // Try firmware vid and pid 50 | if(dev->open(vid, pid, UPDATE_USAGE_PAGE, UPDATE_USAGE)){ 51 | builtin = false; 52 | return true; 53 | } 54 | // Try builtin vid and pid 55 | if(dev->open(vid, boot_pid, UPDATE_USAGE_PAGE, UPDATE_USAGE)){ 56 | builtin = true; 57 | return true; 58 | } 59 | return false; 60 | } 61 | 62 | void ProtoPOK3R::close(){ 63 | dev->close(); 64 | } 65 | 66 | bool ProtoPOK3R::isOpen() const { 67 | return dev->isOpen(); 68 | } 69 | 70 | bool ProtoPOK3R::isBuiltin() { 71 | return builtin; 72 | } 73 | 74 | bool ProtoPOK3R::rebootFirmware(bool reopen){ 75 | if(!builtin){ 76 | // LOG("In Firmware"); 77 | return true; 78 | } 79 | 80 | LOG("Reset to Firmware"); 81 | if(!sendCmd(RESET_CMD, RESET_BOOT_SUBCMD)) 82 | return false; 83 | close(); 84 | 85 | if(reopen){ 86 | ZThread::sleep(REBOOT_SLEEP); 87 | 88 | // Find device with new vid and pid 89 | if(!open()){ 90 | ELOG("open error"); 91 | return false; 92 | } 93 | 94 | if(builtin) 95 | return false; 96 | } 97 | return true; 98 | } 99 | 100 | bool ProtoPOK3R::rebootBootloader(bool reopen){ 101 | if(builtin){ 102 | // LOG("In Bootloader"); 103 | return true; 104 | } 105 | 106 | LOG("Reset to Bootloader"); 107 | if(!sendCmd(RESET_CMD, RESET_BUILTIN_SUBCMD)) 108 | return false; 109 | close(); 110 | 111 | if(reopen){ 112 | ZThread::sleep(REBOOT_SLEEP); 113 | 114 | // Find device with new vid and pid 115 | if(!open()){ 116 | ELOG("open error"); 117 | return false; 118 | } 119 | 120 | if(!builtin) 121 | return false; 122 | } 123 | return true; 124 | } 125 | 126 | bool ProtoPOK3R::getInfo(){ 127 | ZBinary data; 128 | if(!sendRecvCmd(UPDATE_START_CMD, 0, data)) 129 | return false; 130 | 131 | RLOG(data.dumpBytes(4, 8)); 132 | 133 | zu32 a = data.readleu32(); 134 | zu16 fw_addr = data.readleu16(); 135 | zu16 page_size = data.readleu16(); 136 | zu16 e = data.readleu16() + 10; 137 | zu16 f = data.readleu16() + 10; 138 | zu32 ver_addr = data.readleu32(); 139 | 140 | LOG(ZString::ItoS((zu64)a, 16)); 141 | LOG("firmware address: 0x" << HEX(fw_addr)); 142 | LOG("page size?: 0x" << HEX(page_size)); 143 | LOG(e); 144 | LOG(f); 145 | LOG("version_address: 0x" << HEX(ver_addr)); 146 | 147 | return true; 148 | } 149 | 150 | ZString ProtoPOK3R::getVersion(){ 151 | ZBinary bin; 152 | if(!readFlash(VER_ADDR, bin)) 153 | return "ERROR"; 154 | 155 | ZBinary tst; 156 | tst.fill(0xFF, 64); 157 | if(bin == tst) 158 | return "CLEARED"; 159 | 160 | bin.rewind(); 161 | zu32 len = MIN(bin.readleu32(), 64U); 162 | ZString ver = ZString(bin.raw() + 4, len); 163 | return ver; 164 | } 165 | 166 | KBStatus ProtoPOK3R::clearVersion(){ 167 | DLOG("clearVersion"); 168 | if(!rebootBootloader()) 169 | return ERR_IO; 170 | 171 | LOG("Clear Version"); 172 | if(!eraseFlash(VER_ADDR, VER_ADDR + 8)) 173 | return ERR_IO; 174 | 175 | ZBinary bin; 176 | if(!readFlash(VER_ADDR, bin)) 177 | return ERR_IO; 178 | 179 | ZBinary tst; 180 | tst.fill(0xFF, 64); 181 | if(bin != tst) 182 | return ERR_IO; 183 | 184 | return SUCCESS; 185 | } 186 | 187 | KBStatus ProtoPOK3R::setVersion(ZString version){ 188 | DLOG("setVersion " << version); 189 | auto status = clearVersion(); 190 | if(status != SUCCESS) 191 | return status; 192 | 193 | LOG("Writing Version: " << version); 194 | 195 | ZBinary vdata; 196 | zu64 vlen = version.size() + 4; 197 | vdata.fill(0, vlen + (4 - (vlen % 4))); 198 | vdata.writeleu32(version.size()); 199 | vdata.write(version.bytes(), version.size()); 200 | 201 | // write version 202 | if(!writeFlash(VER_ADDR, vdata)){ 203 | LOG("write error"); 204 | return ERR_FAIL; 205 | } 206 | 207 | // check version 208 | ZString nver = getVersion(); 209 | // LOG("New Version: " << nver); 210 | 211 | if(nver != version){ 212 | ELOG("failed to set version"); 213 | return ERR_FLASH; 214 | } 215 | 216 | return SUCCESS; 217 | } 218 | 219 | ZBinary ProtoPOK3R::dumpFlash(){ 220 | ZBinary dump; 221 | zu32 cp = FLASH_LEN / 10; 222 | int perc = 0; 223 | RLOG(perc << "%..."); 224 | for(zu32 addr = 0; addr < FLASH_LEN; addr += 64){ 225 | if(!readFlash(addr, dump)) 226 | break; 227 | 228 | if(addr >= cp){ 229 | perc += 10; 230 | RLOG(perc << "%..."); 231 | cp += FLASH_LEN / 10; 232 | } 233 | } 234 | RLOG("100%" << ZLog::NEWLN); 235 | 236 | return dump; 237 | } 238 | 239 | bool ProtoPOK3R::writeFirmware(const ZBinary &fwbinin){ 240 | ZBinary fwbin = fwbinin; 241 | // Encode the firmware for the POK3R 242 | encode_firmware(fwbin); 243 | 244 | // update reset 245 | ZBinary tmp1; 246 | if(!sendRecvCmd(UPDATE_START_CMD, 0, tmp1)) 247 | return false; 248 | 249 | LOG("Erase..."); 250 | if(!eraseFlash(FW_ADDR, FW_ADDR + fwbin.size())){ 251 | // if(!eraseFlash(VER_ADDR, 0xA108)){ 252 | ELOG("erase error"); 253 | return false; 254 | } 255 | 256 | ZThread::sleep(ERASE_SLEEP); 257 | 258 | // Write firmware 259 | LOG("Write..."); 260 | for(zu64 o = 0; o < fwbin.size(); o += 52){ 261 | ZBinary packet; 262 | fwbin.read(packet, 52); 263 | if(!writeFlash(FW_ADDR + o, packet)){ 264 | LOG("error writing: 0x" << ZString::ItoS(FW_ADDR + o, 16)); 265 | return false; 266 | } 267 | } 268 | 269 | fwbin.rewind(); 270 | 271 | LOG("Check..."); 272 | for(zu64 o = 0; o < fwbin.size(); o += 52){ 273 | ZBinary packet; 274 | fwbin.read(packet, 52); 275 | if(!checkFlash(FW_ADDR + o, packet)){ 276 | LOG("error checking: 0x" << ZString::ItoS(FW_ADDR + o, 16)); 277 | return false; 278 | } 279 | } 280 | 281 | // update reset? 282 | ZBinary tmp2; 283 | if(!sendRecvCmd(UPDATE_START_CMD, 0, tmp2)) 284 | return false; 285 | return true; 286 | } 287 | 288 | bool ProtoPOK3R::readFlash(zu32 addr, ZBinary &bin){ 289 | DLOG("readFlash " << HEX(addr)); 290 | // Send command 291 | ZBinary data; 292 | data.writeleu32(addr); 293 | data.writeleu32(addr + 64); 294 | if(!sendRecvCmd(FLASH_CMD, FLASH_READ_SUBCMD, data)) 295 | return false; 296 | bin.write(data); 297 | return true; 298 | } 299 | 300 | bool ProtoPOK3R::writeFlash(zu32 addr, ZBinary bin){ 301 | DLOG("writeFlash " << HEX(addr) << " " << bin.size()); 302 | if(!bin.size()) 303 | return false; 304 | // Send command 305 | ZBinary arg; 306 | arg.writeleu32(addr); 307 | arg.writeleu32(addr + bin.size() - 1); 308 | arg.write(bin); 309 | if(!sendCmd(FLASH_CMD, FLASH_WRITE_SUBCMD, arg)) 310 | return false; 311 | return true; 312 | } 313 | 314 | bool ProtoPOK3R::checkFlash(zu32 addr, ZBinary bin){ 315 | DLOG("checkFlash " << HEX(addr) << " " << bin.size()); 316 | if(!bin.size()) 317 | return false; 318 | // Send command 319 | ZBinary arg; 320 | arg.writeleu32(addr); 321 | arg.writeleu32(addr + bin.size() - 1); 322 | arg.write(bin); 323 | if(!sendCmd(FLASH_CMD, FLASH_CHECK_SUBCMD, arg)) 324 | return false; 325 | return true; 326 | } 327 | 328 | bool ProtoPOK3R::eraseFlash(zu32 start, zu32 end){ 329 | DLOG("eraseFlash " << HEX(start) << " " << end); 330 | // Send command 331 | ZBinary arg; 332 | arg.writeleu32(start); 333 | arg.writeleu32(end); 334 | if(!sendCmd(ERASE_CMD, 8, arg)) 335 | return false; 336 | return true; 337 | } 338 | 339 | zu16 ProtoPOK3R::crcFlash(zu32 addr, zu32 len){ 340 | // Send command 341 | ZBinary arg; 342 | arg.writeleu32(addr); 343 | arg.writeleu32(len); 344 | sendCmd(CRC_CMD, 0, arg); 345 | return 0; 346 | } 347 | 348 | zu32 ProtoPOK3R::baseFirmwareAddr() const { 349 | return FW_ADDR; 350 | } 351 | 352 | bool ProtoPOK3R::sendCmd(zu8 cmd, zu8 subcmd, ZBinary bin){ 353 | if(bin.size() > 60){ 354 | ELOG("bad data size"); 355 | return false; 356 | } 357 | 358 | ZBinary packet(UPDATE_PKT_LEN); 359 | packet.fill(0); 360 | packet.writeu8(cmd); // command 361 | packet.writeu8(subcmd); // subcommand 362 | packet.seek(4); 363 | packet.write(bin); // data 364 | 365 | packet.seek(2); 366 | zu16 crc = ZHash(packet).hash(); 367 | packet.writeleu16(crc); // CRC 368 | 369 | DLOG("send:"); 370 | DLOG(ZLog::RAW << packet.dumpBytes(4, 8)); 371 | 372 | // Send command (interrupt write) 373 | if(!dev->send(packet, (cmd == RESET_CMD ? true : false))){ 374 | ELOG("send error"); 375 | return false; 376 | } 377 | return true; 378 | } 379 | 380 | bool ProtoPOK3R::sendRecvCmd(zu8 cmd, zu8 subcmd, ZBinary &data){ 381 | if(!sendCmd(cmd, subcmd, data)) 382 | return false; 383 | 384 | // Recv packet 385 | data.resize(UPDATE_PKT_LEN); 386 | if(!dev->recv(data)){ 387 | ELOG("recv error"); 388 | return false; 389 | } 390 | 391 | DLOG("recv:"); 392 | DLOG(ZLog::RAW << data.dumpBytes(4, 8)); 393 | 394 | if(data.size() != UPDATE_PKT_LEN){ 395 | DLOG("bad recv size"); 396 | return false; 397 | } 398 | 399 | data.rewind(); 400 | return true; 401 | } 402 | 403 | // POK3R firmware XOR encryption/decryption key 404 | // Found at 0x2188 in Pok3r flash 405 | static const zu32 xor_key[] = { 406 | 0x55aa55aa, 407 | 0xaa55aa55, 408 | 0x000000ff, 409 | 0x0000ff00, 410 | 0x00ff0000, 411 | 0xff000000, 412 | 0x00000000, 413 | 0xffffffff, 414 | 0x0f0f0f0f, 415 | 0xf0f0f0f0, 416 | 0xaaaaaaaa, 417 | 0x55555555, 418 | 0x00000000, 419 | }; 420 | 421 | // This array was painstakingly translated from a switch with a lot of shifts in the firmware. 422 | // I noticed after the fact that it was identical to the array that Sprite used in his hack, 423 | // but the groups of offsets were in a rotated order. Oh well. 424 | const zu8 swap_key[] = { 425 | 0,1,2,3, 426 | 1,2,3,0, 427 | 2,1,3,0, 428 | 3,2,1,0, 429 | 3,1,0,2, 430 | 1,2,0,3, 431 | 2,3,1,0, 432 | 0,2,1,3, 433 | }; 434 | 435 | void decode_firmware_packet(zbyte *data, zu32 num){ 436 | zu32 *words = (zu32*)data; 437 | 438 | // XOR decryption 439 | for(int i = 0; i < 13; ++i){ 440 | words[i] = words[i] ^ xor_key[i]; 441 | } 442 | 443 | // Swap decryption 444 | zu8 f = (num & 7) << 2; 445 | for(int i = 0; i < 52; i+=4){ 446 | zbyte a = data[i + swap_key[f + 0]]; 447 | zbyte b = data[i + swap_key[f + 1]]; 448 | zbyte c = data[i + swap_key[f + 2]]; 449 | zbyte d = data[i + swap_key[f + 3]]; 450 | 451 | data[i + 0] = a; 452 | data[i + 1] = b; 453 | data[i + 2] = c; 454 | data[i + 3] = d; 455 | } 456 | } 457 | 458 | // Decode the encryption scheme used by the POK3R firmware 459 | // Ripped from the pok3r builtin firmware 460 | void ProtoPOK3R::decode_firmware(ZBinary &bin){ 461 | zu32 count = 0; 462 | for(zu32 offset = 0; offset < bin.size(); offset += 52){ 463 | if(count >= 10 && count <= 100){ 464 | decode_firmware_packet(bin.raw() + offset, count); 465 | } 466 | count++; 467 | } 468 | } 469 | 470 | void encode_firmware_packet(zbyte *data, zu32 num){ 471 | zu32 *words = (zu32*)data; 472 | 473 | // Swap encryption 474 | zu8 f = (num & 7) << 2; 475 | for(int i = 0; i < 52; i+=4){ 476 | zbyte a = data[i + 0]; 477 | zbyte b = data[i + 1]; 478 | zbyte c = data[i + 2]; 479 | zbyte d = data[i + 3]; 480 | 481 | data[i + swap_key[f + 0]] = a; 482 | data[i + swap_key[f + 1]] = b; 483 | data[i + swap_key[f + 2]] = c; 484 | data[i + swap_key[f + 3]] = d; 485 | } 486 | 487 | // XOR encryption 488 | for(int i = 0; i < 13; ++i){ 489 | words[i] = words[i] ^ xor_key[i]; 490 | } 491 | } 492 | 493 | // Encode using the encryption scheme used by the POK3R firmware 494 | // Reverse engineered from the above 495 | void ProtoPOK3R::encode_firmware(ZBinary &bin){ 496 | zu32 count = 0; 497 | for(zu32 offset = 0; offset < bin.size(); offset += 52){ 498 | if(count >= 10 && count <= 100){ 499 | encode_firmware_packet(bin.raw() + offset, count); 500 | } 501 | count++; 502 | } 503 | } 504 | -------------------------------------------------------------------------------- /kbscan.cpp: -------------------------------------------------------------------------------- 1 | #include "kbscan.h" 2 | #include "proto_pok3r.h" 3 | #include "proto_cykb.h" 4 | 5 | #include 6 | 7 | #include "zmap.h" 8 | #include "zset.h" 9 | #include "zlog.h" 10 | 11 | #define INTERFACE_CLASS_HID 3 12 | #define INTERFACE_SUBCLASS_NONE 0 13 | #define INTERFACE_PROTOCOL_NONE 0 14 | 15 | #define HOLTEK_VID 0x04d9 16 | 17 | #define BOOT_PID 0x1000 18 | 19 | #define POK3R_PID 0x0141 20 | #define POK3R_RGB_PID 0x0167 21 | #define POK3R_RGB2_PID 0x0207 22 | #define VORTEX_CORE_PID 0x0175 23 | //#define VORTEX_TESTER_PID 0x0200 24 | #define VORTEX_RACE3_PID 0x0192 25 | #define VORTEX_VIBE_PID 0x0216 26 | #define VORTEX_CYPHER_PID 0x0282 27 | #define VORTEX_TAB60_PID 0x0304 28 | #define VORTEX_TAB75_PID 0x0344 29 | #define VORTEX_TAB90_PID 0x0346 30 | #define KBP_V60_PID 0x0112 31 | #define KBP_V80_PID 0x0129 32 | //#define KBP_V100_PID 0x0 33 | #define TEX_YODA_II_PID 0x0163 34 | #define MISTEL_MD600_PID 0x0143 35 | #define MISTEL_MD200_PID 0x0200 36 | 37 | #define FW_ADDR_2C00 0x2c00 38 | #define FW_ADDR_3200 0x3200 39 | #define FW_ADDR_3400 0x3400 40 | 41 | #define CONSOLE_USAGE_PAGE 0xff31 42 | #define CONSOLE_USAGE 0x0074 43 | 44 | static const ZMap known_devices = { 45 | { DEV_POK3R, { "vortex/pok3r", "Vortex POK3R", HOLTEK_VID, POK3R_PID, BOOT_PID | POK3R_PID, PROTO_POK3R, FW_ADDR_2C00 } }, 46 | { DEV_POK3R_RGB, { "vortex/pok3r_rgb", "Vortex POK3R RGB", HOLTEK_VID, POK3R_RGB_PID, BOOT_PID | POK3R_RGB_PID, PROTO_CYKB, FW_ADDR_3400 } }, 47 | { DEV_POK3R_RGB2, { "vortex/pok3r_rgb2", "Vortex POK3R RGB2", HOLTEK_VID, POK3R_RGB2_PID, BOOT_PID | POK3R_RGB2_PID, PROTO_CYKB, FW_ADDR_3400 } }, 48 | { DEV_VORTEX_CORE, { "vortex/core", "Vortex Core", HOLTEK_VID, VORTEX_CORE_PID, BOOT_PID | VORTEX_CORE_PID, PROTO_CYKB, FW_ADDR_3400 } }, 49 | // { DEV_VORTEX_TESTER, { "vortex/tester", "Vortex Tester", HOLTEK_VID, VORTEX_TESTER_PID, BOOT_PID | VORTEX_TESTER_PID, PROTO_CYKB, FW_ADDR_3400 } }, // same as MD200 50 | { DEV_VORTEX_RACE3, { "vortex/race3", "Vortex Race 3", HOLTEK_VID, VORTEX_RACE3_PID, BOOT_PID | VORTEX_RACE3_PID, PROTO_CYKB, FW_ADDR_3400 } }, 51 | { DEV_VORTEX_VIBE, { "vortex/vibe", "Vortex ViBE", HOLTEK_VID, VORTEX_VIBE_PID, BOOT_PID | VORTEX_VIBE_PID, PROTO_CYKB, FW_ADDR_3400 } }, 52 | { DEV_VORTEX_CYPHER, { "vortex/cypher", "Vortex Cypher", HOLTEK_VID, VORTEX_CYPHER_PID, BOOT_PID | VORTEX_CYPHER_PID, PROTO_CYKB, FW_ADDR_3200 } }, 53 | { DEV_VORTEX_TAB60, { "vortex/tab60", "Vortex Tab 60", HOLTEK_VID, VORTEX_TAB60_PID, BOOT_PID | VORTEX_TAB60_PID, PROTO_CYKB, FW_ADDR_3400 } }, 54 | { DEV_VORTEX_TAB75, { "vortex/tab75", "Vortex Tab 75", HOLTEK_VID, VORTEX_TAB75_PID, BOOT_PID | VORTEX_TAB75_PID, PROTO_CYKB, FW_ADDR_3400 } }, 55 | { DEV_VORTEX_TAB90, { "vortex/tab90", "Vortex Tab 90", HOLTEK_VID, VORTEX_TAB90_PID, BOOT_PID | VORTEX_TAB90_PID, PROTO_CYKB, FW_ADDR_3400 } }, 56 | { DEV_KBP_V60, { "kbp/v60", "KBP V60", HOLTEK_VID, KBP_V60_PID, BOOT_PID | KBP_V60_PID, PROTO_POK3R, FW_ADDR_2C00 } }, 57 | { DEV_KBP_V80, { "kbp/v80", "KBP V80", HOLTEK_VID, KBP_V80_PID, BOOT_PID | KBP_V80_PID, PROTO_POK3R, FW_ADDR_2C00 } }, 58 | // { DEV_KBP_V100, { "kbp/100", "KBP V100", HOLTEK_VID, KBP_V100_PID, BOOT_PID | KBP_V100_PID, PROTO_POK3R, FW_ADDR_2C00 } }, 59 | { DEV_TEX_YODA_II, { "tex/yoda", "Tex Yoda II", HOLTEK_VID, TEX_YODA_II_PID, BOOT_PID | TEX_YODA_II_PID, PROTO_CYKB, FW_ADDR_3400 } }, 60 | { DEV_MISTEL_MD600, { "mistel/md600", "Mistel Barocco MD600", HOLTEK_VID, MISTEL_MD600_PID, BOOT_PID | MISTEL_MD600_PID, PROTO_CYKB, FW_ADDR_3400 } }, 61 | { DEV_MISTEL_MD200, { "mistel/md200", "Mistel Freeboard MD200", HOLTEK_VID, MISTEL_MD200_PID, BOOT_PID | MISTEL_MD200_PID, PROTO_CYKB, FW_ADDR_3400 } }, 62 | }; 63 | 64 | static ZMap known_ids; 65 | 66 | KBScan::KBScan(){ 67 | if(!known_ids.size()){ 68 | for(auto it = known_devices.begin(); it.more(); ++it){ 69 | DeviceInfo info = known_devices[it.get()]; 70 | zu32 id = zu32(info.pid | (info.vid << 16)); 71 | zu32 bid = zu32(info.boot_pid | (info.vid << 16)); 72 | if(known_ids.contains(id)) 73 | LOG("Duplicate known id"); 74 | if(known_ids.contains(bid)) 75 | LOG("Duplicate known boot id"); 76 | known_ids.add(id, it.get()); 77 | known_ids.add(bid, it.get()); 78 | } 79 | } 80 | } 81 | 82 | zu32 KBScan::find(DeviceType devtype){ 83 | if(!known_devices.contains(devtype)){ 84 | ELOG("Unknown device!"); 85 | return 0; 86 | } 87 | DeviceInfo dev = known_devices[devtype]; 88 | 89 | auto filter_func = [this,devtype,dev](rawhid_detail *detail){ 90 | switch(detail->step){ 91 | case RAWHID_STEP_DEV: 92 | return (detail->vid == dev.vid && (detail->pid == dev.pid || detail->pid == dev.boot_pid)); 93 | case RAWHID_STEP_IFACE: 94 | return (detail->ifclass == INTERFACE_CLASS_HID && 95 | detail->subclass == INTERFACE_SUBCLASS_NONE && 96 | detail->protocol == INTERFACE_PROTOCOL_NONE && 97 | (detail->epin_size == 0 || detail->epin_size == 64) && 98 | (detail->epout_size == 0 || detail->epout_size == 64)); 99 | case RAWHID_STEP_REPORT: 100 | return (detail->usage_page == UPDATE_USAGE_PAGE && detail->usage == UPDATE_USAGE); 101 | case RAWHID_STEP_OPEN: 102 | DLOG("OPEN " << ZString::ItoS((zu64)detail->vid, 16, 4) << " " << ZString::ItoS((zu64)detail->pid, 16, 4)); 103 | ZPointer ptr = new HIDDevice(detail->hid); 104 | devices.push({ devtype, dev, ptr, !!(detail->pid & 0x1000) }); 105 | // if hid pointer is taken, MUST return true here or it WILL double-free 106 | return true; 107 | } 108 | return false; 109 | }; 110 | 111 | zu32 devs = HIDDevice::openFilter(filter_func); 112 | DLOG("Found " << devs << " devices"); 113 | return devs; 114 | } 115 | 116 | zu32 KBScan::scan(){ 117 | 118 | auto filter_func = [this](rawhid_detail *detail){ 119 | zu32 id = detail->pid | (detail->vid << 16); 120 | switch(detail->step){ 121 | case RAWHID_STEP_DEV: 122 | // DLOG("DEV " << detail->bus << " " << detail->device << " " << ZString::ItoS((zu64)detail->vid, 16, 4) << " " << ZString::ItoS((zu64)detail->pid, 16, 4)); 123 | DLOG("DEV " << ZString::ItoS((zu64)detail->vid, 16, 4) << " " << ZString::ItoS((zu64)detail->pid, 16, 4)); 124 | // return true; 125 | return known_ids.contains(id); 126 | 127 | case RAWHID_STEP_IFACE: 128 | DLOG("IFACE " << detail->ifnum << ": " << 129 | detail->ifclass << " " << 130 | detail->subclass << " " << 131 | detail->protocol << " " << 132 | detail->epin_size << " " << 133 | detail->epout_size); 134 | return (detail->ifclass == INTERFACE_CLASS_HID && 135 | detail->subclass == INTERFACE_SUBCLASS_NONE && 136 | detail->protocol == INTERFACE_PROTOCOL_NONE && 137 | (detail->epin_size == 0 || detail->epin_size == 64) && 138 | (detail->epout_size == 0 || detail->epout_size == 64)); 139 | 140 | case RAWHID_STEP_REPORT: { 141 | DLOG("USAGE " << ZString::ItoS((zu64)detail->usage_page, 16, 4) << " " << ZString::ItoS((zu64)detail->usage, 16, 2)); 142 | if(detail->usage_page == UPDATE_USAGE_PAGE && detail->usage == UPDATE_USAGE){ 143 | ZBinary rdesc(detail->report_desc, detail->rdesc_len); 144 | DLOG("REPORT " << detail->rdesc_len << ": " << rdesc.strBytes(1)); 145 | return true; 146 | } 147 | return false; 148 | } 149 | 150 | case RAWHID_STEP_OPEN: { 151 | DLOG("OPEN " << 152 | ZString::ItoS((zu64)detail->vid, 16, 4) << " " << 153 | ZString::ItoS((zu64)detail->pid, 16, 4) << " " << 154 | detail->ifnum 155 | ); 156 | if(!known_ids.contains(id)) 157 | return false; 158 | // make high-level wrapper object 159 | ZPointer ptr = new HIDDevice(detail->hid); 160 | devices.push({ 161 | known_ids[id], 162 | known_devices[known_ids[id]], 163 | ptr, 164 | !!(detail->pid & 0x1000) 165 | }); 166 | // if hid pointer is taken, MUST return true here or it WILL double-free 167 | return true; 168 | } 169 | } 170 | return false; 171 | }; 172 | 173 | zu32 devs = HIDDevice::openFilter(filter_func); 174 | DLOG("Found " << devs << " devices"); 175 | return devs; 176 | } 177 | 178 | void KBScan::dbgScan(){ 179 | 180 | auto filter_func = [this](rawhid_detail *detail){ 181 | zu32 id = detail->pid | (detail->vid << 16); 182 | switch(detail->step){ 183 | case RAWHID_STEP_DEV: 184 | if(known_ids.contains(id)){ 185 | LOG("DEV " << ZString::ItoS((zu64)detail->vid, 16, 4) << " " << ZString::ItoS((zu64)detail->pid, 16, 4)); 186 | return true; 187 | } 188 | return false; 189 | 190 | case RAWHID_STEP_IFACE: 191 | return (detail->ifclass == INTERFACE_CLASS_HID && 192 | detail->subclass == INTERFACE_SUBCLASS_NONE && 193 | detail->protocol == INTERFACE_PROTOCOL_NONE); 194 | 195 | case RAWHID_STEP_REPORT: { 196 | if(detail->usage_page == UPDATE_USAGE_PAGE && detail->usage == UPDATE_USAGE){ 197 | LOG(" update"); 198 | } else if(detail->usage_page == CONSOLE_USAGE_PAGE && detail->usage == CONSOLE_USAGE){ 199 | LOG(" console"); 200 | } 201 | return false; 202 | } 203 | 204 | case RAWHID_STEP_OPEN: { 205 | DLOG("OPEN " << 206 | ZString::ItoS((zu64)detail->vid, 16, 4) << " " << 207 | ZString::ItoS((zu64)detail->pid, 16, 4) << " " << 208 | detail->ifnum 209 | ); 210 | if(!known_ids.contains(id)) 211 | return false; 212 | return false; 213 | } 214 | } 215 | return false; 216 | }; 217 | 218 | zu32 devs = HIDDevice::openFilter(filter_func); 219 | DLOG("Found " << devs << " devices"); 220 | } 221 | 222 | ZList KBScan::open(){ 223 | ZList devs; 224 | 225 | // Make protocol interface object for each device 226 | for(auto it = devices.begin(); it.more(); ++it){ 227 | ListDevice ldev = it.get(); 228 | // Check device 229 | if(ldev.hid.get() && ldev.hid->isOpen()){ 230 | ZPointer iface; 231 | // Select protocol 232 | if(ldev.dev.type == PROTO_POK3R){ 233 | iface = new ProtoPOK3R(ldev.dev.vid, ldev.dev.pid, ldev.dev.boot_pid, ldev.boot, ldev.hid); 234 | } else if(ldev.dev.type == PROTO_CYKB){ 235 | iface = new ProtoCYKB(ldev.dev.vid, ldev.dev.pid, ldev.dev.boot_pid, ldev.boot, ldev.hid, ldev.dev.fw_addr); 236 | } else { 237 | ELOG("Unknown protocol"); 238 | continue; 239 | } 240 | 241 | KBDevice kdev; 242 | kdev.devtype = ldev.devtype; 243 | kdev.info = ldev.dev; 244 | kdev.iface = iface; 245 | devs.push(kdev); 246 | 247 | } else { 248 | LOG(ldev.dev.name << " not open"); 249 | } 250 | } 251 | 252 | return devs; 253 | } 254 | 255 | ZPointer KBScan::openConsole(DeviceType devtype){ 256 | if(!known_devices.contains(devtype)){ 257 | ELOG("Unknown device!"); 258 | return 0; 259 | } 260 | DeviceInfo dev = known_devices[devtype]; 261 | ZPointer ptr; 262 | 263 | auto filter_func = [devtype,dev,&ptr](rawhid_detail *detail){ 264 | switch(detail->step){ 265 | case RAWHID_STEP_DEV: 266 | return (!ptr.get() && (detail->vid == dev.vid && (detail->pid == dev.pid || detail->pid == dev.boot_pid))); 267 | case RAWHID_STEP_IFACE: 268 | return (detail->ifclass == INTERFACE_CLASS_HID && 269 | detail->subclass == INTERFACE_SUBCLASS_NONE && 270 | detail->protocol == INTERFACE_PROTOCOL_NONE && 271 | (detail->epin_size == 0 || detail->epin_size == 32) && 272 | (detail->epout_size == 0 || detail->epout_size == 32)); 273 | case RAWHID_STEP_REPORT: 274 | return (detail->usage_page == CONSOLE_USAGE_PAGE && detail->usage == CONSOLE_USAGE); 275 | case RAWHID_STEP_OPEN: 276 | DLOG("OPEN " << ZString::ItoS((zu64)detail->vid, 16, 4) << " " << ZString::ItoS((zu64)detail->pid, 16, 4)); 277 | ptr = new HIDDevice(detail->hid); 278 | // if hid pointer is taken, MUST return true here or it WILL double-free 279 | return true; 280 | } 281 | return false; 282 | }; 283 | 284 | zu32 devs = HIDDevice::openFilter(filter_func); 285 | DLOG("Found " << devs << " devices"); 286 | return ptr; 287 | } 288 | -------------------------------------------------------------------------------- /rawhid/hid_WINDOWS.c: -------------------------------------------------------------------------------- 1 | /* Simple Raw HID functions for Windows - for use with Teensy RawHID example 2 | * http://www.pjrc.com/teensy/rawhid.html 3 | * Copyright (c) 2009 PJRC.COM, LLC 4 | * 5 | * rawhid_open - open 1 or more devices 6 | * rawhid_recv - receive a packet 7 | * rawhid_send - send a packet 8 | * rawhid_close - close a device 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above description, website URL and copyright notice and this permission 18 | * notice shall be included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | * THE SOFTWARE. 27 | * 28 | * Version 1.0: Initial Release 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include "hid.h" 41 | 42 | //typedef struct hid_struct hid_t; 43 | struct hid_struct { 44 | HANDLE handle; 45 | int open; 46 | }; 47 | 48 | static HANDLE rx_event = NULL; 49 | static HANDLE tx_event = NULL; 50 | static CRITICAL_SECTION rx_mutex; 51 | static CRITICAL_SECTION tx_mutex; 52 | 53 | static void print_win32_err(void) 54 | { 55 | char str[1024]; 56 | DWORD err = GetLastError(); 57 | FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 58 | NULL, 59 | err, 60 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 61 | str, sizeof(str), 62 | NULL); 63 | printf("err %lx: %s\n", err, str); 64 | } 65 | 66 | // private functions, not intended to be used from outside this file 67 | static void hid_close(hid_t *hid); 68 | 69 | // rawhid_recv - receive a packet 70 | // Inputs: 71 | // num = device to receive from 72 | // buf = buffer to receive packet 73 | // len = buffer's size 74 | // timeout = time to wait, in milliseconds 75 | // Output: 76 | // number of bytes received, or -1 on error 77 | // 78 | int rawhid_recv(hid_t *hid, void *buf, int len, int timeout) 79 | { 80 | unsigned char tmpbuf[516]; 81 | OVERLAPPED ov; 82 | DWORD n, r; 83 | 84 | if (sizeof(tmpbuf) < len + 1) return -1; 85 | if (!hid || !hid->open) return -1; 86 | EnterCriticalSection(&rx_mutex); 87 | ResetEvent(&rx_event); 88 | memset(&ov, 0, sizeof(ov)); 89 | ov.hEvent = rx_event; 90 | if (!ReadFile(hid->handle, tmpbuf, len + 1, NULL, &ov)) { 91 | if (GetLastError() != ERROR_IO_PENDING) goto return_error; 92 | r = WaitForSingleObject(rx_event, timeout); 93 | if (r == WAIT_TIMEOUT) goto return_timeout; 94 | if (r != WAIT_OBJECT_0) goto return_error; 95 | } 96 | if (!GetOverlappedResult(hid->handle, &ov, &n, FALSE)) goto return_error; 97 | LeaveCriticalSection(&rx_mutex); 98 | if (n <= 0) return -1; 99 | n--; 100 | if (n > len) n = len; 101 | memcpy(buf, tmpbuf + 1, n); 102 | return n; 103 | return_timeout: 104 | CancelIo(hid->handle); 105 | LeaveCriticalSection(&rx_mutex); 106 | return 0; 107 | return_error: 108 | print_win32_err(); 109 | LeaveCriticalSection(&rx_mutex); 110 | return -1; 111 | } 112 | 113 | // rawhid_send - send a packet 114 | // Inputs: 115 | // num = device to transmit to 116 | // buf = buffer containing packet to send 117 | // len = number of bytes to transmit 118 | // timeout = time to wait, in milliseconds 119 | // Output: 120 | // number of bytes sent, or -1 on error 121 | // 122 | int rawhid_send(hid_t *hid, const void *buf, int len, int timeout) 123 | { 124 | unsigned char tmpbuf[516]; 125 | OVERLAPPED ov; 126 | DWORD n, r; 127 | 128 | if (sizeof(tmpbuf) < len + 1) return -1; 129 | if (!hid || !hid->open) return -1; 130 | EnterCriticalSection(&tx_mutex); 131 | ResetEvent(&tx_event); 132 | memset(&ov, 0, sizeof(ov)); 133 | ov.hEvent = tx_event; 134 | tmpbuf[0] = 0; 135 | memcpy(tmpbuf + 1, buf, len); 136 | if(!WriteFile(hid->handle, tmpbuf, len + 1, NULL, &ov)){ 137 | if (GetLastError() != ERROR_IO_PENDING) 138 | goto return_error; 139 | r = WaitForSingleObject(tx_event, timeout); 140 | if (r == WAIT_TIMEOUT) 141 | goto return_timeout; 142 | if (r != WAIT_OBJECT_0) 143 | goto return_error; 144 | } 145 | if(!GetOverlappedResult(hid->handle, &ov, &n, FALSE)) 146 | goto return_error; 147 | LeaveCriticalSection(&tx_mutex); 148 | if (n <= 0){ 149 | return -1; 150 | } 151 | return n - 1; 152 | return_timeout: 153 | CancelIo(hid->handle); 154 | LeaveCriticalSection(&tx_mutex); 155 | return 0; 156 | return_error: 157 | LeaveCriticalSection(&tx_mutex); 158 | return -1; 159 | } 160 | 161 | // rawhid_open - open a device 162 | // 163 | // Inputs: 164 | // vid = Vendor ID, or -1 if any 165 | // pid = Product ID, or -1 if any 166 | // usage_page = top level usage page, or -1 if any 167 | // usage = top level usage number, or -1 if any 168 | // Output: 169 | // device handle 170 | // 171 | hid_t *rawhid_open(int vid, int pid, int usage_page, int usage) 172 | { 173 | GUID guid; 174 | HDEVINFO info; 175 | DWORD index=0, reqd_size; 176 | SP_DEVICE_INTERFACE_DATA iface; 177 | SP_DEVICE_INTERFACE_DETAIL_DATA *details; 178 | HIDD_ATTRIBUTES attrib; 179 | PHIDP_PREPARSED_DATA hid_data; 180 | HIDP_CAPS capabilities; 181 | HANDLE h; 182 | BOOL ret; 183 | 184 | hid_t *hid = NULL; 185 | 186 | if (!rx_event) { 187 | rx_event = CreateEvent(NULL, TRUE, TRUE, NULL); 188 | tx_event = CreateEvent(NULL, TRUE, TRUE, NULL); 189 | InitializeCriticalSection(&rx_mutex); 190 | InitializeCriticalSection(&tx_mutex); 191 | } 192 | HidD_GetHidGuid(&guid); 193 | info = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); 194 | if (info == INVALID_HANDLE_VALUE) return 0; 195 | for (index=0; 1 ;index++) { 196 | iface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); 197 | ret = SetupDiEnumDeviceInterfaces(info, NULL, &guid, index, &iface); 198 | if (!ret) 199 | return hid; 200 | SetupDiGetInterfaceDeviceDetail(info, &iface, NULL, 0, &reqd_size, NULL); 201 | details = (SP_DEVICE_INTERFACE_DETAIL_DATA *)malloc(reqd_size); 202 | if (details == NULL) 203 | continue; 204 | 205 | memset(details, 0, reqd_size); 206 | details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); 207 | ret = SetupDiGetDeviceInterfaceDetail(info, &iface, details, 208 | reqd_size, NULL, NULL); 209 | if (!ret) { 210 | free(details); 211 | continue; 212 | } 213 | h = CreateFile(details->DevicePath, GENERIC_READ|GENERIC_WRITE, 214 | FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, 215 | OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); 216 | free(details); 217 | if (h == INVALID_HANDLE_VALUE) 218 | continue; 219 | attrib.Size = sizeof(HIDD_ATTRIBUTES); 220 | ret = HidD_GetAttributes(h, &attrib); 221 | //printf("vid: %4x\n", attrib.VendorID); 222 | if (!ret || (vid > 0 && attrib.VendorID != vid) || 223 | (pid > 0 && attrib.ProductID != pid) || 224 | !HidD_GetPreparsedData(h, &hid_data)) { 225 | CloseHandle(h); 226 | continue; 227 | } 228 | if (!HidP_GetCaps(hid_data, &capabilities) || 229 | (usage_page > 0 && capabilities.UsagePage != usage_page) || 230 | (usage > 0 && capabilities.Usage != usage)) { 231 | HidD_FreePreparsedData(hid_data); 232 | CloseHandle(h); 233 | continue; 234 | } 235 | HidD_FreePreparsedData(hid_data); 236 | 237 | if(hid){ 238 | hid_close(hid); 239 | return NULL; 240 | } 241 | 242 | hid = (struct hid_struct *)malloc(sizeof(struct hid_struct)); 243 | if (!hid) { 244 | CloseHandle(h); 245 | continue; 246 | } 247 | hid->handle = h; 248 | hid->open = 1; 249 | // add_hid(hid); 250 | return hid; 251 | } 252 | } 253 | 254 | int rawhid_openall(hid_t **hids, int max, int vid, int pid, int usage_page, int usage) 255 | { 256 | GUID guid; 257 | HDEVINFO info; 258 | DWORD index=0, reqd_size; 259 | SP_DEVICE_INTERFACE_DATA iface; 260 | SP_DEVICE_INTERFACE_DETAIL_DATA *details; 261 | HIDD_ATTRIBUTES attrib; 262 | PHIDP_PREPARSED_DATA hid_data; 263 | HIDP_CAPS capabilities; 264 | HANDLE h; 265 | BOOL ret; 266 | int opencount = 0; 267 | 268 | if (!rx_event) { 269 | rx_event = CreateEvent(NULL, TRUE, TRUE, NULL); 270 | tx_event = CreateEvent(NULL, TRUE, TRUE, NULL); 271 | InitializeCriticalSection(&rx_mutex); 272 | InitializeCriticalSection(&tx_mutex); 273 | } 274 | 275 | HidD_GetHidGuid(&guid); 276 | info = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); 277 | if(info == INVALID_HANDLE_VALUE) 278 | return 0; 279 | for(index = 0; 1; index++) { 280 | iface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); 281 | ret = SetupDiEnumDeviceInterfaces(info, NULL, &guid, index, &iface); 282 | if (!ret) 283 | return opencount; 284 | SetupDiGetInterfaceDeviceDetail(info, &iface, NULL, 0, &reqd_size, NULL); 285 | details = (SP_DEVICE_INTERFACE_DETAIL_DATA *)malloc(reqd_size); 286 | if (details == NULL) 287 | continue; 288 | 289 | memset(details, 0, reqd_size); 290 | details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); 291 | ret = SetupDiGetDeviceInterfaceDetail(info, &iface, details, 292 | reqd_size, NULL, NULL); 293 | if (!ret) { 294 | free(details); 295 | continue; 296 | } 297 | h = CreateFile(details->DevicePath, GENERIC_READ|GENERIC_WRITE, 298 | FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, 299 | OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); 300 | free(details); 301 | if (h == INVALID_HANDLE_VALUE) 302 | continue; 303 | attrib.Size = sizeof(HIDD_ATTRIBUTES); 304 | ret = HidD_GetAttributes(h, &attrib); 305 | //printf("vid: %4x\n", attrib.VendorID); 306 | if (!ret || (vid > 0 && attrib.VendorID != vid) || 307 | (pid > 0 && attrib.ProductID != pid) || 308 | !HidD_GetPreparsedData(h, &hid_data)) { 309 | CloseHandle(h); 310 | continue; 311 | } 312 | if (!HidP_GetCaps(hid_data, &capabilities) || 313 | (usage_page > 0 && capabilities.UsagePage != usage_page) || 314 | (usage > 0 && capabilities.Usage != usage)) { 315 | HidD_FreePreparsedData(hid_data); 316 | CloseHandle(h); 317 | continue; 318 | } 319 | HidD_FreePreparsedData(hid_data); 320 | 321 | hids[opencount] = (struct hid_struct *)malloc(sizeof(struct hid_struct)); 322 | hid_t *hid = hids[opencount]; 323 | if (!hid) { 324 | CloseHandle(h); 325 | continue; 326 | } 327 | opencount++; 328 | 329 | hid->handle = h; 330 | hid->open = 1; 331 | 332 | if(opencount == max) return opencount; 333 | } 334 | return opencount; 335 | } 336 | 337 | int rawhid_openall_filter(rawhid_filter_cb cb, void *user) 338 | { 339 | GUID guid; 340 | HDEVINFO info; 341 | DWORD index=0, reqd_size; 342 | SP_DEVICE_INTERFACE_DATA iface; 343 | SP_DEVICE_INTERFACE_DETAIL_DATA *details; 344 | HIDD_ATTRIBUTES attrib; 345 | PHIDP_PREPARSED_DATA hid_data; 346 | HIDP_CAPS capabilities; 347 | HANDLE h; 348 | BOOL ret; 349 | int opencount = 0; 350 | 351 | struct rawhid_detail detail; 352 | memset(&detail, 0, sizeof(struct rawhid_detail)); 353 | 354 | if (!rx_event) { 355 | rx_event = CreateEvent(NULL, TRUE, TRUE, NULL); 356 | tx_event = CreateEvent(NULL, TRUE, TRUE, NULL); 357 | InitializeCriticalSection(&rx_mutex); 358 | InitializeCriticalSection(&tx_mutex); 359 | } 360 | 361 | HidD_GetHidGuid(&guid); 362 | info = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); 363 | if(info == INVALID_HANDLE_VALUE) 364 | return 0; 365 | // loop over device interfaces 366 | for(index = 0; 1; index++) { 367 | iface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); 368 | ret = SetupDiEnumDeviceInterfaces(info, NULL, &guid, index, &iface); 369 | if (!ret) 370 | return opencount; 371 | SetupDiGetInterfaceDeviceDetail(info, &iface, NULL, 0, &reqd_size, NULL); 372 | details = (SP_DEVICE_INTERFACE_DETAIL_DATA *)malloc(reqd_size); 373 | if (details == NULL) 374 | continue; 375 | 376 | memset(details, 0, reqd_size); 377 | details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); 378 | ret = SetupDiGetDeviceInterfaceDetail(info, &iface, details, 379 | reqd_size, NULL, NULL); 380 | if (!ret) { 381 | free(details); 382 | continue; 383 | } 384 | h = CreateFile(details->DevicePath, GENERIC_READ|GENERIC_WRITE, 385 | FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, 386 | OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); 387 | free(details); 388 | if (h == INVALID_HANDLE_VALUE) 389 | continue; 390 | attrib.Size = sizeof(HIDD_ATTRIBUTES); 391 | ret = HidD_GetAttributes(h, &attrib); 392 | //printf("vid: %4x\n", attrib.VendorID); 393 | if (!ret) { 394 | CloseHandle(h); 395 | continue; 396 | } 397 | 398 | // call user callback with device info 399 | detail.step = RAWHID_STEP_DEV; 400 | detail.vid = attrib.VendorID; 401 | detail.pid = attrib.ProductID; 402 | if (!cb(user, &detail)) { 403 | CloseHandle(h); 404 | continue; 405 | } 406 | 407 | if (!HidD_GetPreparsedData(h, &hid_data)) { 408 | CloseHandle(h); 409 | continue; 410 | } 411 | 412 | if (!HidP_GetCaps(hid_data, &capabilities)) { 413 | HidD_FreePreparsedData(hid_data); 414 | CloseHandle(h); 415 | continue; 416 | } 417 | 418 | // call user callback with hid info 419 | detail.step = RAWHID_STEP_REPORT; 420 | detail.usage_page = capabilities.UsagePage; 421 | detail.usage = capabilities.Usage; 422 | if (!cb(user, &detail)) { 423 | HidD_FreePreparsedData(hid_data); 424 | CloseHandle(h); 425 | continue; 426 | } 427 | 428 | HidD_FreePreparsedData(hid_data); 429 | 430 | hid_t *hid = (struct hid_struct *)malloc(sizeof(struct hid_struct)); 431 | if (!hid) { 432 | CloseHandle(h); 433 | continue; 434 | } 435 | 436 | hid->handle = h; 437 | hid->open = 1; 438 | 439 | // call user callback with open hid_t 440 | detail.step = RAWHID_STEP_OPEN; 441 | detail.hid = hid; 442 | if (!cb(user, &detail)) { 443 | CloseHandle(h); 444 | continue; 445 | } 446 | 447 | opencount++; 448 | } 449 | return opencount; 450 | } 451 | 452 | // rawhid_close - close a device 453 | // 454 | // Inputs: 455 | // num = device to close 456 | // Output 457 | // (nothing) 458 | // 459 | void rawhid_close(hid_t *hid) 460 | { 461 | if (!hid || !hid->open) return; 462 | hid_close(hid); 463 | free(hid); 464 | } 465 | 466 | static void hid_close(hid_t *hid) 467 | { 468 | CloseHandle(hid->handle); 469 | hid->handle = NULL; 470 | } 471 | -------------------------------------------------------------------------------- /proto_cykb.cpp: -------------------------------------------------------------------------------- 1 | #include "proto_cykb.h" 2 | #include "zlog.h" 3 | 4 | #define UPDATE_PKT_LEN 64 5 | 6 | #define UPDATE_ERROR 0xaaff 7 | 8 | #define VER_ADDR 0x3000 9 | 10 | #define FLASH_LEN 0x10000 11 | 12 | #define WAIT_SLEEP 5 13 | #define ERASE_SLEEP 2 14 | 15 | #define HEX(A) (ZString::ItoS((zu64)(A), 16)) 16 | 17 | ProtoCYKB::ProtoCYKB(zu16 vid_, zu16 pid_, zu16 boot_pid_) : 18 | ProtoCYKB(vid_, pid_, boot_pid_, false, new HIDDevice, 0) 19 | { 20 | 21 | } 22 | 23 | ProtoCYKB::ProtoCYKB(zu16 vid_, zu16 pid_, zu16 boot_pid_, bool builtin_, ZPointer dev_, zu32 fw_addr_) : 24 | ProtoQMK(PROTO_CYKB, dev_), 25 | builtin(builtin_), debug(false), nop(false), 26 | vid(vid_), pid(pid_), boot_pid(boot_pid_), 27 | fw_addr(fw_addr_) 28 | { 29 | //dev->setStream(true); 30 | } 31 | 32 | ProtoCYKB::~ProtoCYKB(){ 33 | 34 | } 35 | 36 | bool ProtoCYKB::open(){ 37 | // Try firmware vid and pid 38 | if(dev->open(vid, pid, UPDATE_USAGE_PAGE, UPDATE_USAGE)){ 39 | builtin = false; 40 | return true; 41 | } 42 | // Try builtin vid and pid 43 | if(dev->open(vid, boot_pid, UPDATE_USAGE_PAGE, UPDATE_USAGE)){ 44 | builtin = true; 45 | return true; 46 | } 47 | return false; 48 | } 49 | 50 | void ProtoCYKB::close(){ 51 | dev->close(); 52 | } 53 | 54 | bool ProtoCYKB::isOpen() const { 55 | return dev->isOpen(); 56 | } 57 | 58 | bool ProtoCYKB::isBuiltin(){ 59 | return builtin; 60 | } 61 | 62 | bool ProtoCYKB::rebootFirmware(bool reopen){ 63 | if(!builtin){ 64 | // LOG("In Firmware"); 65 | return true; 66 | } 67 | 68 | LOG("Reset to Firmware"); 69 | if(!sendCmd(RESET, RESET_FW)) 70 | return false; 71 | close(); 72 | 73 | if(reopen){ 74 | ZThread::sleep(WAIT_SLEEP); 75 | 76 | if(!open()){ 77 | ELOG("open error"); 78 | return false; 79 | } 80 | 81 | if(builtin) 82 | return false; 83 | } 84 | return true; 85 | } 86 | 87 | bool ProtoCYKB::rebootBootloader(bool reopen){ 88 | if(builtin){ 89 | // LOG("In Bootloader"); 90 | return true; 91 | } 92 | 93 | LOG("Reset to Bootloader"); 94 | if(!sendCmd(RESET, RESET_BL)) 95 | return false; 96 | close(); 97 | 98 | if(reopen){ 99 | ZThread::sleep(WAIT_SLEEP); 100 | 101 | if(!open()){ 102 | ELOG("open error"); 103 | return false; 104 | } 105 | 106 | if(!builtin) 107 | return false; 108 | } 109 | return true; 110 | } 111 | 112 | bool ProtoCYKB::getInfo(){ 113 | ZBinary bin; 114 | ZBinary data; 115 | 116 | for(zu8 i = 0x20; i < 0x23; ++i){ 117 | bin.clear(); 118 | if(!sendRecvCmd(READ, i, bin)) 119 | return false; 120 | data.write(bin.getSub(4, 60)); 121 | } 122 | RLOG(data.dumpBytes(4, 8, VER_ADDR)); 123 | 124 | info_section(data); 125 | 126 | LOG("READ_400"); 127 | bin.clear(); 128 | if(!sendRecvCmd(READ, READ_400, bin)) 129 | return false; 130 | RLOG(bin.getSub(4, 52).dumpBytes(4, 8)); 131 | 132 | LOG("READ_3c00"); 133 | bin.clear(); 134 | if(!sendRecvCmd(READ, READ_3C00, bin)) 135 | return false; 136 | RLOG(bin.getSub(4, 4).dumpBytes(4, 8)); 137 | 138 | return true; 139 | } 140 | 141 | ZString ProtoCYKB::getVersion(){ 142 | DLOG("getVersion"); 143 | 144 | // version 1 145 | ZBinary data; 146 | if(!sendRecvCmd(READ, READ_VER1, data)) 147 | return "ERROR"; 148 | // RLOG(data.dumpBytes(4, 8)); 149 | 150 | ZBinary tst; 151 | tst.fill(0xFF, 60); 152 | 153 | ZString ver; 154 | if(data.getSub(4) == tst){ 155 | ver = "CLEARED"; 156 | } else { 157 | data.seek(4); 158 | zu32 len = MIN(data.readleu32(), 60U); 159 | ver.parseUTF16((zu16 *)(data.raw() + 8), len); 160 | } 161 | DLOG("version: " << ver); 162 | 163 | // version 2 164 | // ZBinary data2; 165 | // if(!sendRecvCmd(READ, READ_VER2, data2)) 166 | // return "ERROR"; 167 | // DLOG("ver2:"); 168 | // DLOG(ZLog::RAW << data2.getSub(4).dumpBytes(4, 8)); 169 | 170 | return ver; 171 | } 172 | 173 | KBStatus ProtoCYKB::clearVersion(){ 174 | DLOG("clearVersion"); 175 | if(!rebootBootloader()) 176 | return ERR_FAIL; 177 | 178 | if(!eraseFlash(VER_ADDR, 0xB4)) 179 | return ERR_FAIL; 180 | 181 | ZBinary data; 182 | if(!sendRecvCmd(READ, READ_VER2, data)) 183 | return ERR_FAIL; 184 | 185 | ZBinary tst; 186 | tst.fill(0xFF, 60); 187 | if(data.getSub(4) != tst){ 188 | ELOG("version not cleared"); 189 | ELOG(ZLog::RAW << data.dumpBytes(4, 8)); 190 | return ERR_FLASH; 191 | } 192 | 193 | return SUCCESS; 194 | } 195 | 196 | const zu32 ver2[15] = { 197 | 0x00800004, 0x00010300, 0x00000041, 0xefffffff, 198 | 0x00000001, 0x00000000, 0x016704d9, 0xffffffff, 199 | 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 200 | 0xffffffff, 0xffffffff, 0x001c5aa5, 201 | }; 202 | 203 | KBStatus ProtoCYKB::setVersion(ZString version){ 204 | DLOG("setVersion " << version); 205 | auto status = clearVersion(); 206 | if(status != SUCCESS) 207 | return status; 208 | 209 | LOG("Writing Version: " << version); 210 | 211 | // UTF-16 encoded version string 212 | zu16 str[256]; 213 | zu64 len = version.readUTF16(str, 255); 214 | str[len++] = 0; 215 | 216 | // Write version string 217 | ZBinary sdata; 218 | sdata.writeleu32(len * 2); 219 | for(zu64 i = 0; i < len; ++i) 220 | sdata.writeleu16(str[i]); 221 | 222 | ZBinary vdata; 223 | vdata.fill(0xFF, 0x78); 224 | vdata.write(sdata); 225 | vdata.seek(0x78); 226 | vdata.write((const zbyte *)ver2, sizeof(ver2)); 227 | // RLOG(vdata.dumpBytes(4, 8)); 228 | 229 | // write version 230 | if(!writeFlash(VER_ADDR, vdata)){ 231 | ELOG("write error"); 232 | return ERR_FAIL; 233 | } 234 | 235 | // check version 236 | ZBinary data; 237 | if(!sendRecvCmd(READ, READ_VER2, data)) 238 | return ERR_FAIL; 239 | ZBinary cdata(ver2, sizeof(ver2)); 240 | if(data.getSub(4) != cdata){ 241 | ELOG("failed to set version"); 242 | return ERR_FLASH; 243 | } 244 | 245 | ZString nver = getVersion(); 246 | // LOG("New Version: " << nver); 247 | if(nver != version){ 248 | ELOG("failed to set version string"); 249 | return ERR_FLASH; 250 | } 251 | 252 | return SUCCESS; 253 | } 254 | 255 | ZBinary ProtoCYKB::dumpFlash(){ 256 | ZBinary dump; 257 | /* 258 | for(zu16 i = 0; i < FLASH_LEN - 60; i += 60){ 259 | if(!readFlash(i, dump)) 260 | return dump; 261 | } 262 | */ 263 | 264 | // readable flash is not a multiple of 60, 265 | // so read the last little bit for a full dump 266 | /* 267 | ZBinary tmp; 268 | if(!readFlash(FLASH_LEN - 60, tmp)) 269 | return dump; 270 | dump.write(tmp.raw() + 44, 16); 271 | */ 272 | 273 | for(zu16 i = 0; i < FLASH_LEN - 60; i += 60){ 274 | if(!readFlash(i, dump)) 275 | return dump; 276 | } 277 | 278 | return dump; 279 | } 280 | 281 | bool ProtoCYKB::writeFirmware(const ZBinary &fwbinin){ 282 | ZBinary fwbin = fwbinin; 283 | // Encode the firmware for the POK3R RGB 284 | encode_firmware(fwbin); 285 | zu32 crc0 = ZHash(fwbinin).hash(); 286 | LOG("Firmware CRC D: " << ZString::ItoS((zu64)crc0, 16, 8)); 287 | zu32 crc1 = ZHash(fwbin).hash(); 288 | LOG("Firmware CRC E: " << ZString::ItoS((zu64)crc1, 16, 8)); 289 | 290 | // zu32 ccrc = crcFlash(fw_addr, 0xc000); 291 | zu32 ccrc = crcFlash(fw_addr, fwbin.size()); 292 | LOG("Current CRC: " << ZString::ItoS((zu64)ccrc, 16, 8)); 293 | 294 | LOG("Erase..."); 295 | if(!eraseFlash(fw_addr, fwbin.size())) 296 | // if(!eraseFlash(fw_addr, FLASH_LEN - fw_addr)) 297 | return false; 298 | 299 | ZThread::sleep(WAIT_SLEEP); 300 | 301 | LOG("Write..."); 302 | if(!writeFlash(fw_addr, fwbin)) 303 | return false; 304 | 305 | zu32 crc2 = crcFlash(fw_addr, fwbin.size()); 306 | LOG("New CRC: " << ZString::ItoS((zu64)crc2, 16, 8)); 307 | 308 | if(crc2 != crc1){ 309 | ELOG("CRCs do not match, firmware write failed"); 310 | return false; 311 | } 312 | 313 | return true; 314 | } 315 | 316 | bool ProtoCYKB::eraseAndCheck(){ 317 | // Reset to bootloader 318 | if(!rebootBootloader()) 319 | return false; 320 | 321 | zu32 crc_before = crcFlash(VER_ADDR, FLASH_LEN - VER_ADDR); 322 | LOG("Current CRC: " << ZString::ItoS((zu64)crc_before, 16, 8)); 323 | 324 | zu32 addr = VER_ADDR; 325 | for(zu32 i = 0; i < 16-3; ++i){ 326 | LOG("Erase 0x" << HEX(addr)); 327 | if(!eraseFlash(addr, 0x1000)){ 328 | ELOG("erase failed"); 329 | return false; 330 | } 331 | addr += 0x1000; 332 | } 333 | 334 | zu32 crc_after = crcFlash(VER_ADDR, FLASH_LEN - VER_ADDR); 335 | LOG("New CRC: " << ZString::ItoS((zu64)crc_after, 16, 8)); 336 | 337 | return true; 338 | } 339 | 340 | void ProtoCYKB::test(){ 341 | ZBinary bin; 342 | 343 | LOG("READ_400"); 344 | bin.clear(); 345 | if(!sendRecvCmd(READ, READ_400, bin)) 346 | return; 347 | RLOG(bin.getSub(4, 52).dumpBytes(4, 8)); 348 | 349 | LOG("READ_3c00"); 350 | bin.clear(); 351 | if(!sendRecvCmd(READ, READ_3C00, bin)) 352 | return; 353 | RLOG(bin.getSub(4, 4).dumpBytes(4, 8)); 354 | 355 | LOG("READ_MODE"); 356 | bin.clear(); 357 | if(!sendRecvCmd(READ, READ_MODE, bin)) 358 | return; 359 | RLOG(bin.getSub(4, 1).dumpBytes(4, 8)); 360 | 361 | if(!rebootBootloader()) 362 | return; 363 | 364 | LOG("READ_MODE"); 365 | bin.clear(); 366 | if(!sendRecvCmd(READ, READ_MODE, bin)) 367 | return; 368 | RLOG(bin.getSub(4, 1).dumpBytes(4, 8)); 369 | 370 | ZBinary data; 371 | data.writeleu32(0x400); 372 | data.writeleu32(0x4da4); 373 | 374 | LOG("SUM"); 375 | if(!sendCmd(FW, FW_SUM, data)) 376 | return; 377 | if(!dev->recv(bin)) 378 | return; 379 | bin.rewind(); 380 | bin.seek(4); 381 | zu32 sum = bin.readleu32(); 382 | LOG("SUM " << ZString::ItoS((zu64)sum, 16)); 383 | 384 | LOG("CRC"); 385 | if(!sendCmd(FW, FW_CRC, data)) 386 | return; 387 | if(!dev->recv(bin)) 388 | return; 389 | bin.rewind(); 390 | bin.seek(4); 391 | LOG("CRC " << ZString::ItoS((zu64)bin.readleu32(), 16)); 392 | } 393 | 394 | bool ProtoCYKB::eraseFlash(zu32 start, zu32 length){ 395 | DLOG("eraseFlash 0x" << HEX(start) << " " << length); 396 | if(start < VER_ADDR){ 397 | ELOG("bad address"); 398 | return false; 399 | } 400 | 401 | ZBinary data; 402 | data.writeleu32(start - VER_ADDR); 403 | data.writeleu32(length); 404 | 405 | if(!sendCmd(FW, FW_ERASE, data)) 406 | return false; 407 | 408 | // give time for erase 409 | ZThread::sleep(ERASE_SLEEP); 410 | 411 | return recvCmd(data); 412 | } 413 | 414 | bool ProtoCYKB::readFlash(zu32 addr, ZBinary &bin){ 415 | DLOG("readFlash 0x" << HEX(addr)); 416 | ZBinary data; 417 | data.writeleu32(addr); 418 | if(!sendRecvCmd(READ, READ_ADDR, data)) 419 | return false; 420 | bin.write(data.raw() + 4, 60); 421 | return true; 422 | } 423 | 424 | bool ProtoCYKB::writeFlash(zu32 addr, ZBinary bin){ 425 | DLOG("writeFlash 0x" << HEX(addr) << " " << bin.size()); 426 | if(addr < VER_ADDR){ 427 | ELOG("bad address"); 428 | return false; 429 | } 430 | 431 | // Set address 432 | ZBinary adata; 433 | adata.writeleu32(addr - VER_ADDR); 434 | if(!sendRecvCmd(ADDR, ADDR_SET, adata)) 435 | return false; 436 | 437 | // Get address 438 | adata.clear(); 439 | if(!sendRecvCmd(ADDR, ADDR_GET, adata)) 440 | return false; 441 | adata.seek(4); 442 | zu32 saddr = adata.readleu32(); 443 | 444 | if(saddr != addr - VER_ADDR){ 445 | ELOG("failed to set write address"); 446 | return false; 447 | } 448 | 449 | // Write 450 | zu16 pos = addr - VER_ADDR; 451 | bin.rewind(); 452 | while(!bin.atEnd()){ 453 | ZBinary data; 454 | bin.read(data, 52); 455 | //bin.read(data, 60); 456 | zu8 sz = data.size(); 457 | DLOG("write " << HEX(VER_ADDR + pos) << ", " << sz << " bytes"); 458 | if(!sendRecvCmd(WRITE, data.size(), data)) 459 | return false; 460 | data.seek(4); 461 | zu16 next = data.readleu16(); 462 | pos += sz; 463 | if(next != pos){ 464 | ELOG("write sequence error " << HEX(next) << " " << HEX(pos)); 465 | } 466 | } 467 | 468 | return true; 469 | } 470 | 471 | zu32 ProtoCYKB::crcFlash(zu32 addr, zu32 len){ 472 | if(addr < VER_ADDR){ 473 | ELOG("bad address"); 474 | return 0; 475 | } 476 | 477 | DLOG("crcFlash 0x" << HEX(addr) << " 0x" << HEX(len)); 478 | 479 | // CRC command 480 | ZBinary data1; 481 | data1.writeleu32(addr - VER_ADDR); 482 | //data1.writeleu32(0); 483 | data1.writeleu32(len); 484 | if(!sendRecvCmd(FW, FW_CRC, data1)) 485 | return 0; 486 | data1.seek(4); 487 | zu32 crc = data1.readleu32(); 488 | LOG("crc " << HEX(crc)); 489 | 490 | // SUM command 491 | ZBinary data2; 492 | data2.writeleu32(addr - VER_ADDR); 493 | //data2.writeleu32(0); 494 | data2.writeleu32(len); 495 | if(!sendRecvCmd(FW, FW_SUM, data2)) 496 | return 0; 497 | data2.seek(4); 498 | zu32 sum = data2.readleu32(); 499 | LOG("sum " << HEX(sum)); 500 | 501 | return crc; 502 | } 503 | 504 | zu32 ProtoCYKB::baseFirmwareAddr() const { 505 | return fw_addr; 506 | } 507 | 508 | bool ProtoCYKB::sendCmd(zu8 cmd, zu8 a1, ZBinary data){ 509 | if(data.size() > 52){ 510 | ELOG("bad data size"); 511 | return false; 512 | } 513 | 514 | ZBinary packet(UPDATE_PKT_LEN); 515 | packet.fill(0); 516 | packet.writeu8(cmd); // command 517 | packet.writeu8(a1); // argument 518 | packet.seek(4); 519 | packet.write(data); // data 520 | 521 | // packet.seek(2); 522 | // zu16 crc = ZHash(packet).hash(); 523 | // packet.writeleu16(crc); // CRC 524 | 525 | DLOG("send:"); 526 | DLOG(ZLog::RAW << packet.dumpBytes(4, 8)); 527 | 528 | // Send packet 529 | if(!dev->send(packet, (cmd == RESET ? true : false))){ 530 | ELOG("send error"); 531 | return false; 532 | } 533 | return true; 534 | } 535 | 536 | bool ProtoCYKB::recvCmd(ZBinary &data){ 537 | // Recv packet 538 | data.resize(UPDATE_PKT_LEN); 539 | if(!dev->recv(data)){ 540 | ELOG("recv error"); 541 | return false; 542 | } 543 | 544 | if(data.size() != UPDATE_PKT_LEN){ 545 | DLOG("bad recv size"); 546 | return false; 547 | } 548 | 549 | DLOG("recv:"); 550 | DLOG(ZLog::RAW << data.dumpBytes(4, 8)); 551 | 552 | // data.seek(2); 553 | // zu16 crc0 = data.readleu16(); 554 | // data.seek(2); 555 | // data.writeleu16(0); 556 | // data.rewind(); 557 | // zu16 crc1 = ZHash(data).hash(); 558 | // DLOG("crc: " << HEX(crc0) << " " << HEX(crc1)); 559 | 560 | // Check error 561 | data.rewind(); 562 | if(data.readleu16() == UPDATE_ERROR){ 563 | DLOG("error response: " << HEX(data[4]) << " " << HEX(data[5])); 564 | DLOG(ZLog::RAW << data.dumpBytes(4, 8)); 565 | return false; 566 | } 567 | data.rewind(); 568 | 569 | return true; 570 | } 571 | 572 | bool ProtoCYKB::sendRecvCmd(zu8 cmd, zu8 a1, ZBinary &data){ 573 | if(!sendCmd(cmd, a1, data)) 574 | return false; 575 | return recvCmd(data); 576 | } 577 | 578 | // POK3R RGB XOR encryption/decryption key 579 | // Somone somewhere thought a random XOR key was any better than the one they 580 | // used in the POK3R firmware. Yeah, good one. 581 | // See fw_xor_decode.c for the hilarious way this key was obtained. 582 | static const zu32 xor_key[] = { 583 | 0xe7c29474, 584 | 0x79084b10, 585 | 0x53d54b0d, 586 | 0xfc1e8f32, 587 | 0x48e81a9b, 588 | 0x773c808e, 589 | 0xb7483552, 590 | 0xd9cb8c76, 591 | 0x2a8c8bc6, 592 | 0x0967ada8, 593 | 0xd4520f5c, 594 | 0xd0c3279d, 595 | 0xeac091c5, 596 | }; 597 | 598 | // Decode the encryption scheme used by the POK3R RGB firmware 599 | // Just XOR encryption with 52-byte key seen above. 600 | void xor_decode_encode(ZBinary &bin){ 601 | // XOR decryption 602 | zu32 *words = (zu32 *)bin.raw(); 603 | for(zu64 i = 0; i < bin.size() / 4; ++i){ 604 | words[i] = words[i] ^ xor_key[i % 13]; 605 | } 606 | } 607 | 608 | void ProtoCYKB::decode_firmware(ZBinary &bin){ 609 | xor_decode_encode(bin); 610 | } 611 | 612 | void ProtoCYKB::encode_firmware(ZBinary &bin){ 613 | xor_decode_encode(bin); 614 | } 615 | 616 | void ProtoCYKB::info_section(ZBinary data){ 617 | ZString ver; 618 | if(data.readleu32() == 0xFFFFFFFF){ 619 | ver = "CLEARED"; 620 | } else { 621 | data.rewind(); 622 | zu32 len = MIN(data.readleu32(), 60U); 623 | ver.parseUTF16((zu16 *)(data.raw() + 4), len); 624 | } 625 | LOG("Version String: " << ver); 626 | 627 | data.seek(120); 628 | zu32 a = data.readleu32(); 629 | zu32 b = data.readleu32(); 630 | zu32 c = data.readleu32(); 631 | zu32 d = data.readleu32(); 632 | zu32 e = data.readleu32(); 633 | zu32 f = data.readleu32(); 634 | zu32 ivid = data.readleu16(); 635 | zu32 ipid = data.readleu16(); 636 | data.seek(176); 637 | zu32 h = data.readleu32(); 638 | 639 | LOG("a: " << ZString::ItoS((zu64)a, 16, 8)); 640 | LOG("Version: " << ZString::ItoS((zu64)b, 16, 8)); 641 | LOG("c: " << ZString::ItoS((zu64)c, 16, 8)); 642 | LOG("d: " << ZString::ItoS((zu64)d, 16, 8)); 643 | LOG("e: " << ZString::ItoS((zu64)e, 16, 8)); 644 | LOG("f: " << ZString::ItoS((zu64)f, 16, 8)); 645 | LOG("VID/PID: " << ZString::ItoS((zu64)ivid, 16, 4) << " " << ZString::ItoS((zu64)ipid, 16, 4)); 646 | LOG("h: " << ZString::ItoS((zu64)h, 16, 8)); 647 | } 648 | -------------------------------------------------------------------------------- /rawhid/hid_LINUX.c: -------------------------------------------------------------------------------- 1 | /* Simple Raw HID functions for Linux - for use with Teensy RawHID example 2 | * http://www.pjrc.com/teensy/rawhid.html 3 | * Copyright (c) 2009 PJRC.COM, LLC 4 | * 5 | * rawhid_open - open 1 or more devices 6 | * rawhid_recv - receive a packet 7 | * rawhid_send - send a packet 8 | * rawhid_close - close a device 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above description, website URL and copyright notice and this permission 18 | * notice shall be included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | * THE SOFTWARE. 27 | * 28 | * Version 1.0: Initial Release 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "hid.h" 38 | 39 | #define INTERFACE_CLASS_HID 3 40 | #define INTERFACE_SUBCLASS_NONE 0 41 | #define INTERFACE_PROTOCOL_NONE 0 42 | 43 | #define DESCRIPTOR_HID 0x2100 44 | #define DESCRIPTOR_REPORT 0x2200 45 | #define DESCRIPTOR_PHYSICAL 0x2300 46 | 47 | #define TAG_USAGE_PAGE 0x4 48 | #define TAG_USAGE 0x8 49 | 50 | // On Linux there are several options to access HID devices. 51 | // 52 | // libusb 0.1 - the only way that works well on all distributions 53 | // libusb 1.0 - someday will become standard on most distributions 54 | // hidraw driver - relatively new, not supported on many distributions (yet) 55 | // hiddev driver - old, ubuntu, fedora, others dropping support 56 | // usbfs - low level usb API: http://www.kernel.org/doc/htmldocs/usb.html#usbfs 57 | // 58 | // This code uses libusb 0.1, which is well supported on all linux distributions 59 | // and very stable and widely used by many programs. libusb 0.1 only provides a 60 | // simple synchronous interface, basically the same as this code needs. However, 61 | // if you want non-blocking I/O, libusb 0.1 simply does not provide that. There 62 | // is also no kernel-level buffering, so performance is poor. 63 | // 64 | // UPDATE: As of November 2011, hidraw support seems to be working well in all 65 | // major linux distributions. Embedded and "small" distros, used on ARM boards, 66 | // routers and embedded hardware stil seem to omit the hidraw driver. 67 | // 68 | // The hidraw driver is a great solution. However, it has only been supported 69 | // by relatively recent (in 2009) kernels. Here is a quick survey of the status 70 | // of hidraw support in various distributions as of Sept 2009: 71 | // 72 | // Fedora 11: works, kernel 2.6.29.4 73 | // Mandiva 2009.1: works, kernel 2.6.29.1 74 | // Ubuntu 9.10-alpha6: works, kernel 2.6.31 75 | // Ubuntu 9.04: sysfs attrs chain broken (hidraw root only), 2.6.28 kernel 76 | // openSUSE 11.1: sysfs attrs chain broken (hidraw root only), 2.6.27.7 kernel 77 | // Debian Live, Lenny 5.0.2: sysfs attrs chain broken (hidraw root only), 2.6.26 78 | // SimplyMEPIS 8.0.10: sysfs attrs chain broken (hidraw root only), 2.6.27 79 | // Mint 7: sysfs attrs chain broken (hidraw root only), 2.6.28 kernel 80 | // Gentoo 2008.0-r1: sysfs attrs chain broken (hidraw root only), 2.6.24 kernel 81 | // Centos 5: no hidraw or hiddev devices, 2.6.18 kernel 82 | // Slitaz 2.0: no hidraw devices (has hiddev), 2.6.25.5 kernel 83 | // Puppy 4.3: no hidraw devices (has hiddev), 2.6.30.5 kernel 84 | // Damn Small 4.4.10: (would not boot) 85 | // Gentoo 10.0-test20090926: (would not boot) 86 | // PCLinuxOS 2009.2: (would not boot) 87 | // Slackware: (no live cd available? www.slackware-live.org dead) 88 | 89 | #define printf(...) // comment this out for lots of info 90 | 91 | struct hid_struct { 92 | usb_dev_handle *usb; 93 | int open; 94 | int iface; 95 | int ep_in; 96 | int ep_out; 97 | int ref_id; 98 | }; 99 | 100 | static int count = 0; 101 | static int handle_refs[128]; 102 | 103 | // private functions, not intended to be used from outside this file 104 | static void hid_close(hid_t *hid); 105 | static int hid_parse_item(uint32_t *val, uint8_t **data, const uint8_t *end); 106 | 107 | // rawhid_recv - receive a packet 108 | // Inputs: 109 | // num = device to receive from 110 | // buf = buffer to receive packet 111 | // len = buffer's size 112 | // timeout = time to wait, in milliseconds 113 | // Output: 114 | // number of bytes received, or -1 on error 115 | // 116 | int rawhid_recv(hid_t *hid, void *buf, int len, int timeout) 117 | { 118 | int r; 119 | 120 | if (!hid || !hid->open) return -1; 121 | printf("recv ep %d\n", hid->ep_in); 122 | r = usb_interrupt_read(hid->usb, hid->ep_in, buf, len, timeout); 123 | //if (r >= 0) return r; 124 | if (r == -110) return 0; // timeout 125 | return r; 126 | } 127 | 128 | // rawhid_send - send a packet 129 | // Inputs: 130 | // num = device to transmit to 131 | // buf = buffer containing packet to send 132 | // len = number of bytes to transmit 133 | // timeout = time to wait, in milliseconds 134 | // Output: 135 | // number of bytes sent, or -1 on error 136 | // 137 | int rawhid_send(hid_t *hid, const void *buf, int len, int timeout) 138 | { 139 | if (!hid || !hid->open) return -1; 140 | printf("send ep %d\n", hid->ep_out); 141 | if (hid->ep_out) { 142 | return usb_interrupt_write(hid->usb, hid->ep_out, buf, len, timeout); 143 | } else { 144 | return usb_control_msg(hid->usb, 0x21, USB_REQ_SET_CONFIGURATION, 0, hid->iface, (void *)buf, len, timeout); 145 | } 146 | } 147 | 148 | struct hid_match { 149 | int max; 150 | int vid; 151 | int pid; 152 | int usage_page; 153 | int usage; 154 | int count; 155 | hid_t **hids; 156 | }; 157 | 158 | static int filter_cb(void *user, struct rawhid_detail *detail) 159 | { 160 | struct hid_match *match = (struct hid_match *)user; 161 | 162 | switch(detail->step){ 163 | case RAWHID_STEP_DEV: 164 | return (match->count < match->max && 165 | detail->vid == match->vid && 166 | detail->pid == match->pid); 167 | case RAWHID_STEP_IFACE: 168 | return (detail->ifclass == INTERFACE_CLASS_HID && 169 | detail->subclass == INTERFACE_SUBCLASS_NONE && 170 | detail->protocol == INTERFACE_PROTOCOL_NONE); 171 | case RAWHID_STEP_REPORT: 172 | return (detail->usage_page == match->usage_page && 173 | detail->usage == match->usage); 174 | case RAWHID_STEP_OPEN: 175 | printf("filter open %x %x %x %x\n", detail->vid, detail->pid, detail->usage_page, detail->usage); 176 | match->hids[match->count] = detail->hid; 177 | match->count++; 178 | return 1; 179 | } 180 | return 0; 181 | } 182 | 183 | // rawhid_open - open a device 184 | // 185 | // Inputs: 186 | // max = maximum number of devices to open 187 | // vid = Vendor ID, or -1 if any 188 | // pid = Product ID, or -1 if any 189 | // usage_page = top level usage page, or -1 if any 190 | // usage = top level usage number, or -1 if any 191 | // Output: 192 | // device handle 193 | // 194 | hid_t *rawhid_open(int vid, int pid, int usage_page, int usage) 195 | { 196 | hid_t *hid; 197 | 198 | struct hid_match match; 199 | match.max = 1; 200 | match.vid = vid; 201 | match.pid = pid; 202 | match.usage_page = usage_page; 203 | match.usage = usage; 204 | match.count = 0; 205 | match.hids = &hid; 206 | 207 | if (rawhid_openall_filter(filter_cb, &match)) 208 | return hid; 209 | return NULL; 210 | } 211 | 212 | int rawhid_openall(hid_t **hids, int max, int vid, int pid, int usage_page, int usage) 213 | { 214 | struct hid_match match; 215 | match.max = max; 216 | match.vid = vid; 217 | match.pid = pid; 218 | match.usage_page = usage_page; 219 | match.usage = usage; 220 | match.count = 0; 221 | match.hids = hids; 222 | 223 | return rawhid_openall_filter(filter_cb, &match); 224 | } 225 | 226 | int rawhid_openall_filter(rawhid_filter_cb cb, void *user) 227 | { 228 | int opencount = 0; 229 | uint8_t buf[1024]; 230 | struct rawhid_detail detail; 231 | memset(&detail, 0, sizeof(struct rawhid_detail)); 232 | 233 | printf("rawhid_open_filter\n"); 234 | usb_init(); 235 | usb_find_busses(); 236 | usb_find_devices(); 237 | // loop over buses 238 | for (struct usb_bus *bus = usb_get_busses(); bus; bus = bus->next) { 239 | // loop over devices 240 | for (struct usb_device *dev = bus->devices; dev; dev = dev->next) { 241 | 242 | // call user callback with device info 243 | detail.step = RAWHID_STEP_DEV; 244 | detail.bus = bus->location; 245 | detail.device = dev->devnum; 246 | detail.vid = dev->descriptor.idVendor; 247 | detail.pid = dev->descriptor.idProduct; 248 | if (!cb(user, &detail)) { 249 | // if false, do not open/inspect device 250 | printf("callback dev false\n"); 251 | continue; 252 | } 253 | 254 | if (!dev->config) continue; 255 | if (dev->config->bNumInterfaces < 1) continue; 256 | printf("device: vid=%04X, pic=%04X, with %d iface\n", dev->descriptor.idVendor, dev->descriptor.idProduct, dev->config->bNumInterfaces); 257 | struct usb_interface *iface = dev->config->interface; 258 | usb_dev_handle *hand = NULL; 259 | int claimed = 0; 260 | int ref_id = -1; 261 | // loop over interfaces 262 | for (int i = 0; i < dev->config->bNumInterfaces && iface; i++, iface++) { 263 | struct usb_interface_descriptor *desc = iface->altsetting; 264 | if (!desc) continue; 265 | const int ifnum = desc->bInterfaceNumber; 266 | printf(" iface %d type %d, %d, %d\n", ifnum, desc->bInterfaceClass, desc->bInterfaceSubClass, desc->bInterfaceProtocol); 267 | 268 | printf(" endpoints: %d\n", desc->bNumEndpoints); 269 | struct usb_endpoint_descriptor *ep = desc->endpoint; 270 | int ep_in = 0, ep_out = 0, epin_size = 0, epout_size = 0; 271 | // loop over endpoints 272 | for (int n = 0; n < desc->bNumEndpoints; n++, ep++) { 273 | int epnum = ep->bEndpointAddress & 0x7F; 274 | if (ep->bEndpointAddress & 0x80) { 275 | printf(" IN endpoint %d (%d)\n", epnum, ep->wMaxPacketSize); 276 | if (!ep_in){ 277 | ep_in = epnum; 278 | epin_size = ep->wMaxPacketSize; 279 | } 280 | } else { 281 | printf(" OUT endpoint %d (%d)\n", epnum, ep->wMaxPacketSize); 282 | if (!ep_out){ 283 | ep_out = epnum; 284 | epout_size = ep->wMaxPacketSize; 285 | } 286 | } 287 | } 288 | 289 | // call user callback with interface info 290 | detail.step = RAWHID_STEP_IFACE; 291 | detail.ifnum = ifnum; 292 | detail.ifclass = desc->bInterfaceClass; 293 | detail.subclass = desc->bInterfaceSubClass; 294 | detail.protocol = desc->bInterfaceProtocol; 295 | detail.ep_in = ep_in; 296 | detail.ep_out = ep_out; 297 | detail.epin_size = epin_size; 298 | detail.epout_size = epout_size; 299 | if (!cb(user, &detail)) { 300 | printf("callback iface false\n"); 301 | continue; 302 | } 303 | 304 | if (!ep_in) continue; 305 | // open device if not already open 306 | if (!hand) { 307 | hand = usb_open(dev); 308 | if (!hand) { 309 | printf(" unable to open device: %s\n", usb_strerror()); 310 | break; 311 | } 312 | } 313 | // unbind kernel drivers from interface 314 | if (usb_get_driver_np(hand, ifnum, (char *)buf, sizeof(buf)) >= 0) { 315 | printf(" in use by driver \"%s\"\n", buf); 316 | if (usb_detach_kernel_driver_np(hand, ifnum) < 0) { 317 | printf(" unable to detach from kernel\n"); 318 | continue; 319 | } 320 | } 321 | if (usb_claim_interface(hand, ifnum) < 0) { 322 | printf(" unable claim interface %d\n", ifnum); 323 | continue; 324 | } 325 | // hid report descriptor request 326 | int len = usb_control_msg(hand, USB_ENDPOINT_IN | USB_RECIP_INTERFACE, USB_REQ_GET_DESCRIPTOR, DESCRIPTOR_REPORT, ifnum, (char *)buf, sizeof(buf), 250); 327 | printf(" hid descriptor, len=%d\n", len); 328 | if (len < 2) { 329 | usb_release_interface(hand, ifnum); 330 | continue; 331 | } 332 | uint8_t *p = buf; 333 | uint32_t val = 0, parsed_usage_page = 0, parsed_usage = 0; 334 | int tag; 335 | while ((tag = hid_parse_item(&val, &p, buf + len)) >= 0) { 336 | printf(" tag: %X, val %X\n", tag, val); 337 | if (tag == TAG_USAGE_PAGE) parsed_usage_page = val; 338 | if (tag == TAG_USAGE) parsed_usage = val; 339 | if (parsed_usage_page && parsed_usage) break; 340 | } 341 | if ((!parsed_usage_page) || (!parsed_usage)) { 342 | usb_release_interface(hand, ifnum); 343 | continue; 344 | } 345 | 346 | // call user callback with hid info 347 | detail.step = RAWHID_STEP_REPORT; 348 | detail.report_desc = buf; 349 | detail.rdesc_len = len; 350 | detail.usage_page = parsed_usage_page; 351 | detail.usage = parsed_usage; 352 | if (!cb(user, &detail)) { 353 | usb_release_interface(hand, ifnum); 354 | printf("callback report false\n"); 355 | continue; 356 | } 357 | 358 | hid_t *hid = (struct hid_struct *)malloc(sizeof(struct hid_struct)); 359 | if (!hid) { 360 | usb_release_interface(hand, ifnum); 361 | continue; 362 | } 363 | 364 | if (ref_id < 0) { 365 | ref_id = count; 366 | handle_refs[ref_id] = 0; 367 | } 368 | 369 | // call user callback with open hid_t 370 | hid->usb = hand; 371 | hid->iface = ifnum; 372 | hid->ep_in = ep_in; 373 | hid->ep_out = ep_out; 374 | hid->open = 1; 375 | hid->ref_id = ref_id; 376 | 377 | detail.step = RAWHID_STEP_OPEN; 378 | detail.hid = hid; 379 | if (!cb(user, &detail)) { 380 | usb_release_interface(hand, ifnum); 381 | printf("callback open false\n"); 382 | continue; 383 | } 384 | 385 | opencount++; 386 | claimed++; 387 | count++; 388 | handle_refs[ref_id]++; 389 | } 390 | // close device if opened and not needed 391 | if (hand && !claimed) { 392 | usb_close(hand); 393 | } 394 | } 395 | } 396 | return opencount; 397 | } 398 | 399 | // rawhid_close - close a device 400 | // 401 | // Inputs: 402 | // num = device to close 403 | // Output 404 | // (nothing) 405 | // 406 | void rawhid_close(hid_t *hid) 407 | { 408 | if (!hid || !hid->open) return; 409 | hid_close(hid); 410 | free(hid); 411 | } 412 | 413 | static void hid_close(hid_t *hid) 414 | { 415 | hid_t *p; 416 | 417 | usb_release_interface(hid->usb, hid->iface); 418 | 419 | if (hid->ref_id >= 0) { 420 | handle_refs[hid->ref_id]--; 421 | if (handle_refs[hid->ref_id] == 0) { 422 | usb_close(hid->usb); 423 | hid->ref_id = -1; 424 | } 425 | } 426 | count--; 427 | hid->usb = NULL; 428 | } 429 | 430 | // Chuck Robey wrote a real HID report parser 431 | // (chuckr@telenix.org) chuckr@chuckr.org 432 | // http://people.freebsd.org/~chuckr/code/python/uhidParser-0.2.tbz 433 | // this tiny thing only needs to extract the top-level usage page 434 | // and usage, and even then is may not be truly correct, but it does 435 | // work with the Teensy Raw HID example. 436 | static int hid_parse_item(uint32_t *val, uint8_t **data, const uint8_t *end) 437 | { 438 | const uint8_t *p = *data; 439 | uint8_t tag; 440 | int table[4] = {0, 1, 2, 4}; 441 | int len; 442 | 443 | if (p >= end) return -1; 444 | if (p[0] == 0xFE) { 445 | // long item, HID 1.11, 6.2.2.3, page 27 446 | if (p + 5 >= end || p + p[1] >= end) return -1; 447 | tag = p[2]; 448 | *val = 0; 449 | len = p[1] + 5; 450 | } else { 451 | // short item, HID 1.11, 6.2.2.2, page 26 452 | tag = p[0] & 0xFC; 453 | len = table[p[0] & 0x03]; 454 | if (p + len + 1 >= end) return -1; 455 | switch (p[0] & 0x03) { 456 | case 3: *val = p[1] | (p[2] << 8) | (p[3] << 16) | (p[4] << 24); break; 457 | case 2: *val = p[1] | (p[2] << 8); break; 458 | case 1: *val = p[1]; break; 459 | case 0: *val = 0; break; 460 | } 461 | } 462 | *data += len + 1; 463 | return tag; 464 | } 465 | -------------------------------------------------------------------------------- /rawhid/hid_MACOSX.c: -------------------------------------------------------------------------------- 1 | /* Simple Raw HID functions for Linux - for use with Teensy RawHID example 2 | * http://www.pjrc.com/teensy/rawhid.html 3 | * Copyright (c) 2009 PJRC.COM, LLC 4 | * 5 | * rawhid_open - open 1 or more devices 6 | * rawhid_recv - receive a packet 7 | * rawhid_send - send a packet 8 | * rawhid_close - close a device 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above description, website URL and copyright notice and this permission 18 | * notice shall be included in all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | * THE SOFTWARE. 27 | * 28 | * Version 1.0: Initial Release 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "hid.h" 40 | 41 | #define BUFFER_SIZE 64 42 | 43 | #define printf(...) // comment this out to get lots of info printed 44 | 45 | //typedef struct hid_struct hid_t; 46 | typedef struct buffer_struct buffer_t; 47 | 48 | struct hid_struct { 49 | IOHIDDeviceRef ref; 50 | int open; 51 | uint8_t buffer[BUFFER_SIZE]; 52 | buffer_t *first_buffer; 53 | buffer_t *last_buffer; 54 | struct hid_struct *prev; 55 | struct hid_struct *next; 56 | }; 57 | 58 | struct buffer_struct { 59 | struct buffer_struct *next; 60 | uint32_t len; 61 | uint8_t buf[BUFFER_SIZE]; 62 | }; 63 | 64 | struct callback_context { 65 | rawhid_filter_cb cb; 66 | void *user; 67 | struct rawhid_detail detail; 68 | int opencount; 69 | }; 70 | 71 | static int count = 0; 72 | IOHIDManagerRef hid_manager = NULL; 73 | 74 | // private functions, not intended to be used from outside this file 75 | static void hid_close(hid_t *); 76 | static void timeout_callback(CFRunLoopTimerRef, void *); 77 | static void input_callback(void *, IOReturn, void *, IOHIDReportType, 78 | uint32_t, uint8_t *, CFIndex); 79 | 80 | static int hid_init() 81 | { 82 | // Start the HID Manager 83 | // http://developer.apple.com/technotes/tn2007/tn2187.html 84 | if (!hid_manager) { 85 | hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); 86 | if (hid_manager == NULL || CFGetTypeID(hid_manager) != IOHIDManagerGetTypeID()) { 87 | if (hid_manager) 88 | CFRelease(hid_manager); 89 | return 1; 90 | } 91 | } 92 | return 0; 93 | } 94 | 95 | static void input_callback(void *context, IOReturn ret, void *sender, 96 | IOHIDReportType type, uint32_t id, uint8_t *data, CFIndex len) 97 | { 98 | buffer_t *n; 99 | hid_t *hid; 100 | 101 | printf("input_callback\n"); 102 | if (ret != kIOReturnSuccess || len < 1) return; 103 | hid = context; 104 | if (!hid || hid->ref != sender) return; 105 | n = (buffer_t *)malloc(sizeof(buffer_t)); 106 | if (!n) return; 107 | if (len > BUFFER_SIZE) len = BUFFER_SIZE; 108 | memcpy(n->buf, data, len); 109 | n->len = len; 110 | n->next = NULL; 111 | if (!hid->first_buffer || !hid->last_buffer) { 112 | hid->first_buffer = hid->last_buffer = n; 113 | } else { 114 | hid->last_buffer->next = n; 115 | hid->last_buffer = n; 116 | } 117 | CFRunLoopStop(CFRunLoopGetCurrent()); 118 | } 119 | 120 | static void timeout_callback(CFRunLoopTimerRef timer, void *info) 121 | { 122 | printf("timeout_callback\n"); 123 | *(int *)info = 1; 124 | CFRunLoopStop(CFRunLoopGetCurrent()); 125 | } 126 | 127 | // rawhid_recv - receive a packet 128 | // Inputs: 129 | // num = device to receive from 130 | // buf = buffer to receive packet 131 | // len = buffer's size 132 | // timeout = time to wait, in milliseconds 133 | // Output: 134 | // number of bytes received, or -1 on error 135 | // 136 | int rawhid_recv(hid_t *hid, void *buf, int len, int timeout) 137 | { 138 | buffer_t *b; 139 | CFRunLoopTimerRef timer=NULL; 140 | CFRunLoopTimerContext context; 141 | int ret=0, timeout_occurred=0; 142 | 143 | if (len < 1) return 0; 144 | if (!hid || !hid->open) return -1; 145 | if ((b = hid->first_buffer) != NULL) { 146 | if (len > b->len) len = b->len; 147 | memcpy(buf, b->buf, len); 148 | hid->first_buffer = b->next; 149 | free(b); 150 | return len; 151 | } 152 | memset(&context, 0, sizeof(context)); 153 | context.info = &timeout_occurred; 154 | timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + 155 | (double)timeout / 1000.0, 0, 0, 0, timeout_callback, &context); 156 | CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode); 157 | while (1) { 158 | CFRunLoopRun(); 159 | if ((b = hid->first_buffer) != NULL) { 160 | if (len > b->len) len = b->len; 161 | memcpy(buf, b->buf, len); 162 | hid->first_buffer = b->next; 163 | free(b); 164 | ret = len; 165 | break; 166 | } 167 | if (!hid->open) { 168 | printf("rawhid_recv, device not open\n"); 169 | ret = -1; 170 | break; 171 | } 172 | if (timeout_occurred) break; 173 | } 174 | CFRunLoopTimerInvalidate(timer); 175 | CFRelease(timer); 176 | return ret; 177 | } 178 | 179 | static void output_callback(void *context, IOReturn ret, void *sender, 180 | IOHIDReportType type, uint32_t id, uint8_t *data, CFIndex len) 181 | { 182 | printf("output_callback, r=%d\n", ret); 183 | if (ret == kIOReturnSuccess) { 184 | *(int *)context = len; 185 | } else { 186 | // timeout if not success? 187 | *(int *)context = 0; 188 | } 189 | CFRunLoopStop(CFRunLoopGetCurrent()); 190 | } 191 | 192 | // rawhid_send - send a packet 193 | // Inputs: 194 | // num = device to transmit to 195 | // buf = buffer containing packet to send 196 | // len = number of bytes to transmit 197 | // timeout = time to wait, in milliseconds 198 | // Output: 199 | // number of bytes sent, or -1 on error 200 | // 201 | int rawhid_send(hid_t *hid, const void *buf, int len, int timeout) 202 | { 203 | int result=-100; 204 | 205 | if (!hid || !hid->open) return -1; 206 | #if 1 207 | #warning "Send timeout not implemented on MACOSX" 208 | IOReturn ret = IOHIDDeviceSetReport(hid->ref, kIOHIDReportTypeOutput, 0, buf, len); 209 | result = (ret == kIOReturnSuccess) ? len : -1; 210 | #endif 211 | #if 0 212 | // No matter what I tried this never actually sends an output 213 | // report and output_callback never gets called. Why?? 214 | // Did I miss something? This is exactly the same params as 215 | // the sync call that works. Is it an Apple bug? 216 | // (submitted to Apple on 22-sep-2009, problem ID 7245050) 217 | // 218 | IOHIDDeviceScheduleWithRunLoop(hid->ref, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); 219 | // should already be scheduled with run loop by attach_callback, 220 | // sadly this doesn't make any difference either way 221 | 222 | // could this be related? 223 | // http://lists.apple.com/archives/usb/2008/Aug/msg00021.html 224 | // 225 | IOHIDDeviceSetReportWithCallback(hid->ref, kIOHIDReportTypeOutput, 226 | 0, buf, len, (double)timeout / 1000.0, output_callback, &result); 227 | while (1) { 228 | printf("enter run loop (send)\n"); 229 | CFRunLoopRun(); 230 | printf("leave run loop (send)\n"); 231 | if (result > -100) break; 232 | if (!hid->open) { 233 | result = -1; 234 | break; 235 | } 236 | } 237 | #endif 238 | return result; 239 | } 240 | 241 | static void detach_callback(void *context, IOReturn r, void *hid_mgr, IOHIDDeviceRef dev) 242 | { 243 | hid_t *hid; 244 | 245 | printf("detach callback\n"); 246 | // for (hid = first_hid; hid; hid = hid->next) { 247 | // if (hid->ref == dev) { 248 | // hid->open = 0; 249 | // CFRunLoopStop(CFRunLoopGetCurrent()); 250 | // return; 251 | // } 252 | // } 253 | } 254 | 255 | static Boolean IOHIDDevice_GetLongProperty(IOHIDDeviceRef inDeviceRef, CFStringRef inKey, long *outValue) 256 | { 257 | Boolean result = FALSE; 258 | 259 | CFTypeRef tCFTypeRef = IOHIDDeviceGetProperty(inDeviceRef, inKey); 260 | if (tCFTypeRef) { 261 | // if this is a number 262 | if (CFNumberGetTypeID() == CFGetTypeID(tCFTypeRef)) { 263 | // get its value 264 | result = CFNumberGetValue((CFNumberRef)tCFTypeRef, kCFNumberSInt32Type, outValue); 265 | } 266 | } 267 | return result; 268 | } 269 | 270 | static Boolean IOHIDDevice_GetDataProperty(IOHIDDeviceRef inDeviceRef, CFStringRef inKey, uint8_t *data, long *size) 271 | { 272 | Boolean result = FALSE; 273 | 274 | CFTypeRef tCFTypeRef = IOHIDDeviceGetProperty(inDeviceRef, inKey); 275 | if (tCFTypeRef) { 276 | // if this is a number 277 | if (CFDataGetTypeID() == CFGetTypeID(tCFTypeRef)) { 278 | // get its value 279 | long len = CFDataGetLength((CFDataRef)tCFTypeRef); 280 | if (*size >= len){ 281 | CFRange range; 282 | range.location = 0; 283 | range.length = len; 284 | CFDataGetBytes((CFDataRef)tCFTypeRef, range, data); 285 | *size = len; 286 | } 287 | } 288 | } 289 | return result; 290 | } 291 | 292 | static Boolean IOHIDDevice_GetDataPtrProperty(IOHIDDeviceRef inDeviceRef, CFStringRef inKey, const unsigned char **data, long *size) 293 | { 294 | Boolean result = FALSE; 295 | 296 | CFTypeRef tCFTypeRef = IOHIDDeviceGetProperty(inDeviceRef, inKey); 297 | if (tCFTypeRef) { 298 | // if this is a number 299 | if (CFDataGetTypeID() == CFGetTypeID(tCFTypeRef)) { 300 | // get its value 301 | *data = CFDataGetBytePtr((CFDataRef)tCFTypeRef); 302 | *size = CFDataGetLength((CFDataRef)tCFTypeRef); 303 | } 304 | } 305 | return result; 306 | } 307 | 308 | static void attach_callback(void *context, IOReturn r, void *hid_mgr, IOHIDDeviceRef dev) 309 | { 310 | printf("attach callback\n"); 311 | 312 | hid_t **hidp = (hid_t **)context; 313 | if (!hidp) return; 314 | 315 | // open device 316 | if (IOHIDDeviceOpen(dev, kIOHIDOptionsTypeNone) != kIOReturnSuccess) 317 | return; 318 | 319 | hid_t *hid = (hid_t *)malloc(sizeof(hid_t)); 320 | if (!hid) return; 321 | memset(hid, 0, sizeof(hid_t)); 322 | 323 | // register device 324 | IOHIDDeviceScheduleWithRunLoop(dev, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); 325 | IOHIDDeviceRegisterInputReportCallback(dev, hid->buffer, sizeof(hid->buffer), input_callback, hid); 326 | hid->ref = dev; 327 | hid->open = 1; 328 | 329 | *hidp = hid; 330 | 331 | count++; 332 | } 333 | 334 | // rawhid_open - open a device 335 | // 336 | // Inputs: 337 | // max = maximum number of devices to open 338 | // vid = Vendor ID, or -1 if any 339 | // pid = Product ID, or -1 if any 340 | // usage_page = top level usage page, or -1 if any 341 | // usage = top level usage number, or -1 if any 342 | // Output: 343 | // device handle 344 | // 345 | hid_t *rawhid_open(int vid, int pid, int usage_page, int usage) 346 | { 347 | CFMutableDictionaryRef dict; 348 | CFNumberRef num; 349 | IOReturn ret; 350 | hid_t *hid; 351 | 352 | printf("rawhid_open\n"); 353 | if (hid_init()) return NULL; 354 | 355 | if (vid > 0 || pid > 0 || usage_page > 0 || usage > 0) { 356 | // Tell the HID Manager what type of devices we want 357 | dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, 358 | &kCFTypeDictionaryKeyCallBacks, 359 | &kCFTypeDictionaryValueCallBacks); 360 | if (!dict) 361 | return NULL; 362 | if (vid > 0) { 363 | num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &vid); 364 | CFDictionarySetValue(dict, CFSTR(kIOHIDVendorIDKey), num); 365 | CFRelease(num); 366 | } 367 | if (pid > 0) { 368 | num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &pid); 369 | CFDictionarySetValue(dict, CFSTR(kIOHIDProductIDKey), num); 370 | CFRelease(num); 371 | } 372 | if (usage_page > 0) { 373 | num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage_page); 374 | CFDictionarySetValue(dict, CFSTR(kIOHIDPrimaryUsagePageKey), num); 375 | CFRelease(num); 376 | } 377 | if (usage > 0) { 378 | num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage); 379 | CFDictionarySetValue(dict, CFSTR(kIOHIDPrimaryUsageKey), num); 380 | CFRelease(num); 381 | } 382 | IOHIDManagerSetDeviceMatching(hid_manager, dict); 383 | CFRelease(dict); 384 | } else { 385 | IOHIDManagerSetDeviceMatching(hid_manager, NULL); 386 | } 387 | // set up a callbacks for device attach & detach 388 | IOHIDManagerScheduleWithRunLoop(hid_manager, 389 | CFRunLoopGetCurrent(), 390 | kCFRunLoopDefaultMode); 391 | IOHIDManagerRegisterDeviceMatchingCallback(hid_manager, attach_callback, &hid); 392 | IOHIDManagerRegisterDeviceRemovalCallback(hid_manager, detach_callback, NULL); 393 | ret = IOHIDManagerOpen(hid_manager, kIOHIDOptionsTypeNone); 394 | if (ret != kIOReturnSuccess) { 395 | IOHIDManagerUnscheduleFromRunLoop(hid_manager, 396 | CFRunLoopGetCurrent(), 397 | kCFRunLoopDefaultMode); 398 | CFRelease(hid_manager); 399 | return NULL; 400 | } 401 | printf("run loop\n"); 402 | // let it do the callback for all devices 403 | while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true) == kCFRunLoopRunHandledSource) ; 404 | 405 | return hid; 406 | } 407 | 408 | static void match_filter_callback(void *context, IOReturn r, void *hid_mgr, IOHIDDeviceRef dev) 409 | { 410 | printf("attach callback\n"); 411 | 412 | struct callback_context *ctx = (struct callback_context *)context; 413 | if (!ctx) return; 414 | 415 | // get vid/pid 416 | long result; 417 | IOHIDDevice_GetLongProperty(dev, CFSTR(kIOHIDVendorIDKey), &result); 418 | ctx->detail.vid = (unsigned short)(result & 0xFFFF); 419 | IOHIDDevice_GetLongProperty(dev, CFSTR(kIOHIDProductIDKey), &result); 420 | ctx->detail.pid = (unsigned short)(result & 0xFFFF); 421 | 422 | // callback with device info 423 | ctx->detail.step = RAWHID_STEP_DEV; 424 | if (!ctx->cb(ctx->user, &ctx->detail)) { 425 | return; 426 | } 427 | 428 | // open device 429 | if (IOHIDDeviceOpen(dev, kIOHIDOptionsTypeNone) != kIOReturnSuccess) 430 | return; 431 | 432 | // get usage 433 | IOHIDDevice_GetLongProperty(dev, CFSTR(kIOHIDPrimaryUsagePageKey), &result); 434 | ctx->detail.usage_page = (unsigned short)(result & 0xFFFF); 435 | IOHIDDevice_GetLongProperty(dev, CFSTR(kIOHIDPrimaryUsageKey), &result); 436 | ctx->detail.usage = (unsigned short)(result & 0xFFFF); 437 | 438 | // get report descriptor 439 | const unsigned char *data; 440 | long len; 441 | IOHIDDevice_GetDataPtrProperty(dev, CFSTR(kIOHIDReportDescriptorKey), &data, &len); 442 | ctx->detail.report_desc = data; 443 | ctx->detail.rdesc_len = len; 444 | 445 | // callback with report info 446 | ctx->detail.step = RAWHID_STEP_REPORT; 447 | if (!ctx->cb(ctx->user, &ctx->detail)) { 448 | return; 449 | } 450 | 451 | hid_t *hid = (hid_t *)malloc(sizeof(hid_t)); 452 | if (!hid) return; 453 | memset(hid, 0, sizeof(hid_t)); 454 | 455 | // register device 456 | IOHIDDeviceScheduleWithRunLoop(dev, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); 457 | IOHIDDeviceRegisterInputReportCallback(dev, hid->buffer, sizeof(hid->buffer), input_callback, hid); 458 | hid->ref = dev; 459 | hid->open = 1; 460 | 461 | // callback with open hid_t 462 | ctx->detail.step = RAWHID_STEP_OPEN; 463 | ctx->detail.hid = hid; 464 | if (!ctx->cb(ctx->user, &ctx->detail)) { 465 | rawhid_close(hid); 466 | return; 467 | } 468 | 469 | ctx->opencount++; 470 | count++; 471 | } 472 | 473 | int rawhid_openall_filter(rawhid_filter_cb cb, void *user) 474 | { 475 | CFMutableDictionaryRef dict; 476 | CFNumberRef num; 477 | IOReturn ret; 478 | 479 | struct callback_context ctx; 480 | ctx.cb = cb; 481 | ctx.user = user; 482 | ctx.opencount = 0; 483 | memset(&ctx.detail, 0, sizeof(struct rawhid_detail)); 484 | 485 | printf("rawhid_openll_filter\n"); 486 | if (hid_init()) return 0; 487 | // match all 488 | IOHIDManagerSetDeviceMatching(hid_manager, NULL); 489 | 490 | IOHIDManagerScheduleWithRunLoop(hid_manager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); 491 | // set up a callbacks for device match and remove 492 | IOHIDManagerRegisterDeviceMatchingCallback(hid_manager, match_filter_callback, &ctx); 493 | IOHIDManagerRegisterDeviceRemovalCallback(hid_manager, detach_callback, &ctx); 494 | 495 | ret = IOHIDManagerOpen(hid_manager, kIOHIDOptionsTypeNone); 496 | if (ret != kIOReturnSuccess) { 497 | IOHIDManagerUnscheduleFromRunLoop(hid_manager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); 498 | CFRelease(hid_manager); 499 | return 0; 500 | } 501 | 502 | printf("run loop\n"); 503 | // let it do the callback for all devices 504 | while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true) == kCFRunLoopRunHandledSource) ; 505 | 506 | return ctx.opencount; 507 | } 508 | 509 | // rawhid_close - close a device 510 | // 511 | // Inputs: 512 | // num = device to close 513 | // Output 514 | // (nothing) 515 | // 516 | void rawhid_close(hid_t *hid) 517 | { 518 | if (!hid || !hid->open) return; 519 | hid_close(hid); 520 | hid->open = 0; 521 | free(hid); 522 | } 523 | 524 | static void hid_close(hid_t *hid) 525 | { 526 | if (!hid || !hid->open || !hid->ref) return; 527 | IOHIDDeviceUnscheduleFromRunLoop(hid->ref, CFRunLoopGetCurrent( ), kCFRunLoopDefaultMode); 528 | IOHIDDeviceClose(hid->ref, kIOHIDOptionsTypeNone); 529 | hid->ref = NULL; 530 | } 531 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "kbscan.h" 2 | #include "proto_pok3r.h" 3 | #include "proto_cykb.h" 4 | #include "keymap.h" 5 | #include "updatepackage.h" 6 | 7 | #include "zlog.h" 8 | #include "zfile.h" 9 | #include "zpointer.h" 10 | #include "zmap.h" 11 | #include "zoptions.h" 12 | #include "zjson.h" 13 | using namespace LibChaos; 14 | 15 | #include 16 | 17 | // Types 18 | // //////////////////////////////// 19 | 20 | struct Param { 21 | bool ok; 22 | ZArray args; 23 | DeviceType device; 24 | }; 25 | 26 | // Constants 27 | // //////////////////////////////// 28 | 29 | const ZMap devnames = { 30 | { "pok3r", DEV_POK3R }, 31 | 32 | { "rgb", DEV_POK3R_RGB }, 33 | { "pok3r-rgb", DEV_POK3R_RGB }, 34 | { "pok3r_rgb", DEV_POK3R_RGB }, 35 | 36 | { "rgb2", DEV_POK3R_RGB2 }, 37 | { "pok3r-rgb2", DEV_POK3R_RGB2 }, 38 | { "pok3r_rgb2", DEV_POK3R_RGB2 }, 39 | 40 | { "core", DEV_VORTEX_CORE }, 41 | { "vortex-core", DEV_VORTEX_CORE }, 42 | { "vortex_core", DEV_VORTEX_CORE }, 43 | 44 | { "race3", DEV_VORTEX_RACE3 }, 45 | { "vortex-race3", DEV_VORTEX_RACE3 }, 46 | { "vortex_race3", DEV_VORTEX_RACE3 }, 47 | 48 | { "vibe", DEV_VORTEX_VIBE }, 49 | { "vortex-vibe", DEV_VORTEX_VIBE }, 50 | { "vortex_vibe", DEV_VORTEX_VIBE }, 51 | 52 | { "cypher", DEV_VORTEX_CYPHER }, 53 | 54 | { "tab60", DEV_VORTEX_TAB60 }, 55 | { "tab75", DEV_VORTEX_TAB75 }, 56 | { "tab90", DEV_VORTEX_TAB90 }, 57 | 58 | { "kbpv60", DEV_KBP_V60 }, 59 | { "kbp-v60", DEV_KBP_V60 }, 60 | { "kbp_v60", DEV_KBP_V60 }, 61 | 62 | { "kbpv80", DEV_KBP_V80 }, 63 | { "kbp-v80", DEV_KBP_V80 }, 64 | { "kbp_v80", DEV_KBP_V80 }, 65 | 66 | { "yoda2", DEV_TEX_YODA_II }, 67 | { "tex-yoda-2", DEV_TEX_YODA_II }, 68 | { "tex_yoda_2", DEV_TEX_YODA_II }, 69 | { "tex-yoda-ii", DEV_TEX_YODA_II }, 70 | { "tex_yoda_ii", DEV_TEX_YODA_II }, 71 | 72 | { "md600", DEV_MISTEL_MD600 }, 73 | { "barocco", DEV_MISTEL_MD600 }, 74 | 75 | { "md200", DEV_MISTEL_MD200 }, 76 | { "freeboard", DEV_MISTEL_MD200 }, 77 | }; 78 | 79 | // Functions 80 | // //////////////////////////////// 81 | 82 | ZPointer openDevice(DeviceType dev){ 83 | KBScan scanner; 84 | if(!scanner.find(dev)){ 85 | LOG("No device found, check connection and permissions"); 86 | return nullptr; 87 | } 88 | 89 | auto devs = scanner.open(); 90 | if(devs.size() == 1){ 91 | auto kb = devs.front(); 92 | if(kb.iface->isOpen()){ 93 | LOG("Opened " << kb.info.name << 94 | (kb.iface->isBuiltin() ? " (bootloader)" : "") << 95 | (kb.iface->isQMK() ? " [QMK]" : "") 96 | ); 97 | return kb.iface; 98 | } else { 99 | ELOG("Device found but not opened: " << kb.info.name); 100 | } 101 | } else if(devs.size() > 1){ 102 | ELOG("Multiple identical devices found, disconnect devices other than target"); 103 | } else { 104 | ELOG("No device to open?"); 105 | } 106 | return nullptr; 107 | } 108 | 109 | void warning(){ 110 | ELOG("WARNING: THIS TOOL IS RELATIVELY UNTESTED, AND HAS A VERY REAL " 111 | "RISK OF CORRUPTING YOUR KEYBOARD, MAKING IT UNUSABLE WITHOUT " 112 | "EXPENSIVE DEVELOPMENT TOOLS. PROCEED AT YOUR OWN RISK."); 113 | ELOG("Type \"OK\" to continue:"); 114 | std::string str; 115 | std::getline(std::cin, str); 116 | if(str != "OK"){ 117 | LOG("Exiting..."); 118 | exit(EXIT_FAILURE); 119 | } else { 120 | LOG("Proceeding..."); 121 | } 122 | } 123 | 124 | // Commands 125 | // //////////////////////////////// 126 | 127 | int cmd_list(Param *param){ 128 | LOG("List Devices..."); 129 | 130 | KBScan scanner; 131 | scanner.scan(); 132 | auto devs = scanner.open(); 133 | for (auto it = devs.begin(); it.more(); ++it){ 134 | LOG(it.get().info.name << 135 | (it.get().iface->isBuiltin() ? " (bootloader)" : "") << 136 | (it.get().iface->isQMK() ? " [QMK]" : "") << 137 | ": " << it.get().iface->getVersion()); 138 | } 139 | 140 | return 0; 141 | } 142 | 143 | int cmd_version(Param *param){ 144 | // Read Version 145 | ZPointer kb = openDevice(param->device); 146 | if(kb.get()){ 147 | LOG("Version: " << kb->getVersion()); 148 | return 0; 149 | } 150 | return -1; 151 | } 152 | 153 | int cmd_setversion(Param *param){ 154 | if(!param->ok) 155 | warning(); 156 | 157 | ZString version = param->args[1]; 158 | // Set Version 159 | ZPointer kb = openDevice(param->device); 160 | if(kb.get()){ 161 | LOG("Old Version: " << kb->getVersion()); 162 | LOG(kb->setVersion(version)); 163 | LOG(kb->rebootFirmware()); 164 | return 0; 165 | } 166 | return -1; 167 | } 168 | 169 | int cmd_info(Param *param){ 170 | if(!param->ok) 171 | warning(); 172 | 173 | // Get Info 174 | ZPointer kb = openDevice(param->device); 175 | if(kb.get()){ 176 | if(kb->isQMK()){ 177 | ProtoQMK *qmk = dynamic_cast(kb.get()); 178 | LOG(qmk->qmkInfo()); 179 | } else { 180 | LOG(kb->getInfo()); 181 | } 182 | return 0; 183 | } 184 | return -1; 185 | } 186 | 187 | int cmd_reboot(Param *param){ 188 | // Reset to Firmware 189 | ZPointer kb = openDevice(param->device); 190 | if(kb.get()){ 191 | LOG(kb->rebootFirmware(false)); 192 | // Read version 193 | // LOG("Version: " << kb->getVersion()); 194 | return 0; 195 | } 196 | return -1; 197 | } 198 | 199 | int cmd_bootloader(Param *param){ 200 | // Reset to Bootloader 201 | ZPointer kb = openDevice(param->device); 202 | if(kb.get()){ 203 | LOG(kb->rebootBootloader(false)); 204 | // Read version 205 | // LOG("Version: " << kb->getVersion()); 206 | return 0; 207 | } 208 | return -1; 209 | } 210 | 211 | int cmd_dump(Param *param){ 212 | if(!param->ok) 213 | warning(); 214 | 215 | ZPath out = param->args[1]; 216 | // Dump Flash 217 | ZPointer kb = openDevice(param->device); 218 | if(kb.get()){ 219 | LOG("Dump Flash"); 220 | ZBinary bin = kb->dumpFlash(); 221 | // RLOG(bin.dumpBytes(4, 8)); 222 | LOG("Out: " << out << ", " << bin.size() << " bytes"); 223 | ZFile::writeBinary(out, bin); 224 | return 0; 225 | } 226 | return -1; 227 | } 228 | 229 | int cmd_flash(Param *param){ 230 | if(!param->ok) 231 | warning(); 232 | 233 | ZString version = param->args[1]; 234 | ZPath firmware = param->args[2]; 235 | // Update Firmware 236 | if(param->device == 0){ 237 | LOG("Please specifiy a device"); 238 | return 2; 239 | } 240 | ZPointer kb = openDevice(param->device); 241 | if(kb.get()){ 242 | LOG("Update Firmware: " << firmware); 243 | ZBinary fwbin; 244 | ZFile file; 245 | if(!file.open(firmware)){ 246 | ELOG("Failed to open file"); 247 | return -3; 248 | } 249 | if(file.read(fwbin, file.fileSize()) != file.fileSize()){ 250 | ELOG("Failed to read file"); 251 | return -4; 252 | } 253 | LOG(kb->update(version, fwbin)); 254 | return 0; 255 | } 256 | return -1; 257 | } 258 | 259 | int cmd_wipe(Param *param){ 260 | if(!param->ok) 261 | warning(); 262 | 263 | // Erase Firmware 264 | if(param->device == 0){ 265 | LOG("Please specifiy a device"); 266 | return 2; 267 | } 268 | ZPointer kb = openDevice(param->device); 269 | if(kb.get()){ 270 | LOG("Erase Firmware"); 271 | bool ret = kb->eraseAndCheck(); 272 | LOG(ret); 273 | return 0; 274 | } 275 | return -1; 276 | } 277 | 278 | int cmd_decode(Param *param){ 279 | UpdatePackage package; 280 | if(!package.loadFromExe(param->args[1], 0)){ 281 | ELOG("Load Error: " << param->args[1]); 282 | return 1; 283 | } 284 | ZPath out = param->args[2]; 285 | LOG("Write " << out); 286 | ZBinary fw = package.getFirmware(); 287 | if(!ZFile::writeBinary(out, fw)){ 288 | ELOG("Write Error"); 289 | return 2; 290 | } 291 | LOG("Done"); 292 | return 0; 293 | } 294 | 295 | int cmd_eeprom(Param *param){ 296 | ZPointer kb = openDevice(param->device); 297 | if(kb.get()){ 298 | if(!kb->isQMK()){ 299 | ELOG("Not a QMK keyboard!"); 300 | return -2; 301 | } 302 | ProtoQMK *qmk = dynamic_cast(kb.get()); 303 | 304 | if(param->args[1] == "dump"){ 305 | if(param->args.size() != 3){ 306 | ELOG("Usage: pok3rtool eeprom dump "); 307 | return -2; 308 | } 309 | ZPath out = param->args[2]; 310 | LOG("Dump EEPROM"); 311 | ZBinary bin = qmk->dumpEEPROM(); 312 | LOG("Out: " << out << ", " << bin.size() << " bytes"); 313 | ZFile::writeBinary(out, bin); 314 | 315 | } else if(param->args[1] == "erase"){ 316 | if(param->args.size() != 3){ 317 | ELOG("Usage: pok3rtool eeprom erase "); 318 | return -2; 319 | } 320 | zu32 addr = param->args[2].toUint(16); 321 | LOG("Erase EEPROM Page 0x" << ZString::ItoS((zu64)addr, 16)); 322 | LOG(qmk->eraseEEPROM(addr)); 323 | 324 | } else if(param->args[1] == "wipe"){ 325 | LOG("Wipe EEPROM"); 326 | for(zu32 i = 0; i < 128; ++i){ 327 | LOG(qmk->eraseEEPROM(i << 12)); 328 | ZThread::msleep(500); 329 | } 330 | 331 | } else if(param->args[1] == "keymap"){ 332 | if(param->args.size() != 2){ 333 | ELOG("Usage: pok3rtool eeprom keymap"); 334 | return -2; 335 | } 336 | LOG("EEPROM Keymap"); 337 | ZBinary eebin; 338 | for(zu32 addr = QMK_EE_KEYM_PAGE; addr < QMK_EE_KEYM_PAGE + QMK_EE_PAGE_SIZE; addr += 60){ 339 | if(!qmk->readEEPROM(addr, eebin)) 340 | break; 341 | } 342 | zassert(eebin.size() > QMK_EE_PAGE_SIZE); 343 | eebin.resize(QMK_EE_PAGE_SIZE); 344 | RLOG(eebin.dumpBytes(4, 8, QMK_EE_KEYM_PAGE)); 345 | 346 | } else if(param->args[1] == "test"){ 347 | LOG("EEPROM Test"); 348 | bool ret = qmk->eepromTest(); 349 | LOG(ret); 350 | 351 | } else { 352 | LOG("Usage: pok3rtool eeprom"); 353 | } 354 | return 0; 355 | } 356 | return -1; 357 | } 358 | 359 | int cmd_keymap(Param *param){ 360 | ZPointer kb = openDevice(param->device); 361 | if(kb.get()){ 362 | if(!kb->isQMK()){ 363 | ELOG("Not a QMK keyboard!"); 364 | return -2; 365 | } 366 | ProtoQMK *qmk = dynamic_cast(kb.get()); 367 | 368 | if(param->args[1] == "dump"){ 369 | qmk->keymapDump(); 370 | 371 | } else if(param->args[1] == "knownlayouts"){ 372 | for(auto it = Keymap::getKnownLayouts().cbegin(); it.more(); ++it){ 373 | LOG(it.get()); 374 | } 375 | 376 | } else if(param->args[1] == "set"){ 377 | if(param->args.size() != 5 && param->args.size() != 6){ 378 | ELOG("Usage: pok3rtool keymap set [ | ] "); 379 | return -2; 380 | } 381 | 382 | auto keymap = qmk->loadKeymap(); 383 | if(!keymap.get()){ 384 | ELOG("Unable to load keymap"); 385 | return -3; 386 | } 387 | 388 | zu8 layer; 389 | zu16 kp; 390 | ZString keycode; 391 | if(param->args.size() == 5){ 392 | layer = param->args[2].toUint(); 393 | kp = param->args[3].toUint(); 394 | keycode = param->args[4]; 395 | } else if(param->args.size() == 6){ 396 | layer = param->args[2].toUint(); 397 | kp = keymap->layoutRC2K(param->args[3].toUint(), param->args[4].toUint()); 398 | keycode = param->args[5]; 399 | } else { 400 | return -4; 401 | } 402 | 403 | Keymap::keycode kc = keymap->toKeycode(keycode); 404 | 405 | LOG("Keymap Set: layer " << layer << ", key " << kp << " -> " << keymap->keycodeName(kc)); 406 | 407 | keymap->set(layer, kp, kc); 408 | //keymap->printLayers(); 409 | LOG(qmk->uploadKeymap(keymap)); 410 | 411 | } else if(param->args[1] == "commit"){ 412 | if(param->args.size() != 2){ 413 | ELOG("Usage: pok3rtool keymap commit"); 414 | return -2; 415 | } 416 | LOG("Commit Keymap"); 417 | LOG(qmk->commitKeymap()); 418 | 419 | } else if(param->args[1] == "reload"){ 420 | if(param->args.size() != 2){ 421 | ELOG("Usage: pok3rtool keymap reload"); 422 | return -2; 423 | } 424 | LOG("Reload Keymap"); 425 | LOG(qmk->reloadKeymap()); 426 | 427 | } else if(param->args[1] == "reset"){ 428 | if(param->args.size() != 2){ 429 | ELOG("Usage: pok3rtool keymap reset"); 430 | return -2; 431 | } 432 | LOG("Reset Keymap"); 433 | LOG(qmk->resetKeymap()); 434 | 435 | } else if(param->args[1] == "layouts"){ 436 | LOG("Layouts"); 437 | ZArray layouts; 438 | qmk->getLayouts(layouts); 439 | for(zu8 i = 0; i < layouts.size(); ++i){ 440 | LOG(i << ": " << layouts[i]); 441 | } 442 | 443 | } else if(param->args[1] == "setlayout"){ 444 | if(param->args.size() != 3){ 445 | ELOG("Usage: pok3rtool keymap setlayout "); 446 | return -2; 447 | } 448 | ZString layout = param->args[2]; 449 | ZArray layouts; 450 | qmk->getLayouts(layouts); 451 | for(zu8 i = 0; i < layouts.size(); ++i){ 452 | if(layouts[i] == layout){ 453 | LOG("Set Layout " << layout); 454 | LOG(qmk->setLayout(i)); 455 | return 0; 456 | } 457 | } 458 | LOG("No Such Layout " << layout); 459 | return -3; 460 | 461 | } else { 462 | LOG("Usage: pok3rtool keymap"); 463 | } 464 | return 0; 465 | } 466 | return -1; 467 | } 468 | 469 | int cmd_console(Param *param){ 470 | while(true){ 471 | ZPointer con = KBScan::openConsole(param->device); 472 | if(con.get()){ 473 | LOG("Opened console"); 474 | 475 | ZBinary bin; 476 | while(true){ 477 | if(con->recv(bin)){ 478 | RLOG(bin.asChar()); 479 | } else { 480 | ELOG("Error"); 481 | break; 482 | } 483 | } 484 | 485 | return 0; 486 | } 487 | } 488 | } 489 | 490 | // Main 491 | // //////////////////////////////// 492 | 493 | #define OPT_OK "ok" 494 | #define OPT_VERBOSE "verbose" 495 | #define OPT_TYPE "device" 496 | 497 | const ZArray optdef = { 498 | { OPT_OK, 0, ZOptions::NONE }, 499 | { OPT_VERBOSE, 'v', ZOptions::NONE}, 500 | { OPT_TYPE, 't', ZOptions::STRING }, 501 | }; 502 | 503 | typedef int (*cmd_func)(Param *); 504 | struct CmdEntry { 505 | cmd_func func; 506 | unsigned argmin; 507 | unsigned argmax; 508 | ZString usage; 509 | }; 510 | 511 | const ZMap cmds = { 512 | { "list", { cmd_list, 0, 0, "list" } }, 513 | { "version", { cmd_version, 0, 0, "version" } }, 514 | { "setversion", { cmd_setversion, 1, 1, "setversion " } }, 515 | { "info", { cmd_info, 0, 0, "info" } }, 516 | { "reboot", { cmd_reboot, 0, 0, "reboot" } }, 517 | { "bootloader", { cmd_bootloader, 0, 0, "bootloader" } }, 518 | { "dump", { cmd_dump, 1, 1, "dump " } }, 519 | { "flash", { cmd_flash, 2, 2, "flash " } }, 520 | { "wipe", { cmd_wipe, 0, 0, "wipe" } }, 521 | { "decode", { cmd_decode, 2, 2, "decode " } }, 522 | { "eeprom", { cmd_eeprom, 1, 2, "eeprom [arg]" } }, 523 | { "keymap", { cmd_keymap, 1, 5, "keymap [arg]" } }, 524 | { "console", { cmd_console, 0, 0, "console" } }, 525 | }; 526 | 527 | void printUsage(){ 528 | LOG("Pok3rTool Usage:"); 529 | for(auto it = cmds.begin(); it.more(); ++it){ 530 | LOG(" pok3rtool " << cmds[it.get()].usage); 531 | } 532 | } 533 | 534 | #define TERM_RESET "\x1b[m" 535 | #define TERM_RED "\x1b[31m" 536 | #define TERM_PURPLE "\x1b[35m" 537 | 538 | int main(int argc, char **argv){ 539 | // Log files 540 | ZPath lgf = ZPath("logs") + ZLog::genLogFileName("pok3rtool_"); 541 | ZLog::defaultWorker()->logLevelFile(ZLog::INFO, lgf, "[%clock%] %thread% N %log%"); 542 | ZLog::defaultWorker()->logLevelFile(ZLog::DEBUG, lgf, "[%clock%] %thread% D [%function%|%file%:%line%] %log%"); 543 | ZLog::defaultWorker()->logLevelFile(ZLog::ERRORS, lgf, "[%clock%] %thread% E [%function%|%file%:%line%] %log%"); 544 | 545 | ZString optbuf; 546 | for(int i = 0; i < argc; ++i){ 547 | optbuf += argv[i]; 548 | optbuf += " "; 549 | } 550 | DLOG("Command Line: " << optbuf); 551 | 552 | ZOptions options(optdef); 553 | if(!options.parse(argc, argv)) 554 | return -2; 555 | 556 | // Console log 557 | if(options.getOpts().contains(OPT_VERBOSE)){ 558 | ZLog::defaultWorker()->logLevelStdOut(ZLog::INFO, "[%clock%] N %log%"); 559 | ZLog::defaultWorker()->logLevelStdOut(ZLog::DEBUG, TERM_PURPLE "[%clock%] D %log%" TERM_RESET); 560 | ZLog::defaultWorker()->logLevelStdErr(ZLog::ERRORS, TERM_RED "[%clock%] E %log%" TERM_RESET); 561 | } else { 562 | ZLog::defaultWorker()->logLevelStdOut(ZLog::INFO, "%log%"); 563 | ZLog::defaultWorker()->logLevelStdErr(ZLog::ERRORS, TERM_RED "%log%" TERM_RESET); 564 | } 565 | 566 | Param param; 567 | param.device = DEV_NONE; 568 | param.ok = options.getOpts().contains(OPT_OK); 569 | param.args = options.getArgs(); 570 | 571 | if(options.getOpts().contains(OPT_TYPE)){ 572 | ZString type = options.getOpts()[OPT_TYPE]; 573 | if(devnames.contains(type)) 574 | param.device = devnames[type]; 575 | } 576 | 577 | if(param.args.size()){ 578 | ZString cmstr = param.args[0]; 579 | if(cmds.contains(cmstr)){ 580 | CmdEntry cmd = cmds[cmstr]; 581 | if((param.args.size() >= cmd.argmin + 1) && (param.args.size() <= cmd.argmax + 1)){ 582 | try { 583 | return cmd.func(¶m); 584 | } catch(const ZException &e){ 585 | ELOG("ERROR: " << e.what()); 586 | ELOG("Trace: " << e.traceStr()); 587 | #if 1 588 | } catch(const zexception &e){ 589 | ELOG("FATAL: " << e.what); 590 | #endif 591 | } 592 | return -2; 593 | } else { 594 | LOG("Usage: pok3rtool " << cmd.usage); 595 | return -1; 596 | } 597 | } else { 598 | LOG("Unknown Command \"" << cmstr << "\""); 599 | printUsage(); 600 | return -1; 601 | } 602 | } else { 603 | LOG("No Command"); 604 | printUsage(); 605 | return -1; 606 | } 607 | } 608 | -------------------------------------------------------------------------------- /updatepackage.cpp: -------------------------------------------------------------------------------- 1 | #include "updatepackage.h" 2 | #include "proto_pok3r.h" 3 | #include "proto_cykb.h" 4 | 5 | #include "zfile.h" 6 | #include "zhash.h" 7 | #include "zmap.h" 8 | #include "zlog.h" 9 | 10 | typedef int (*decodeFunc)(ZFile *, ZBinary &); 11 | int decode_maajonsn(ZFile *file, ZBinary &fw_out); 12 | int decode_maav101(ZFile *file, ZBinary &fw_out); 13 | int decode_maav102(ZFile *file, ZBinary &fw_out); 14 | int decode_maav105(ZFile *file, ZBinary &fw_out); 15 | int decode_kbp_v60(ZFile *file, ZBinary &fw_out); 16 | int decode_kbp_v80(ZFile *file, ZBinary &fw_out); 17 | 18 | enum PackType { 19 | PACKAGE_NONE = 0, 20 | MAAJONSN, // .maajonsn 21 | MAAV101, // .maaV101 22 | MAAV102, // .maaV102 23 | MAAV105, // .maaV105 24 | KBPV60, 25 | KBPV80, 26 | }; 27 | 28 | const ZMap packages = { 29 | // POK3R (141) 30 | { 0x62FCF913A689C9AE, MAAJONSN }, // V1.1.3 31 | { 0xFE37430DB1FFCF5F, MAAJONSN }, // V1.1.4 32 | { 0x8986F7893143E9F7, MAAJONSN }, // V1.1.5 33 | { 0xA28E5EFB3F796181, MAAJONSN }, // V1.1.6 34 | { 0xEA55CB190C35505F, MAAJONSN }, // V1.1.7 pok3r/v117 35 | 36 | // POK3R RGB (167) 37 | { 0x882CB0E4ECE25454, MAAV102 }, // V1.02.04 38 | { 0x6CFF0BB4F4086C2F, MAAV102 }, // V1.03.00 pok3r_rgb/v130 39 | { 0xA6EE37F856CD24C1, MAAV102 }, // V1.04.00 pok3r_rgb/v140 40 | 41 | // Vortex CORE (175) 42 | { 0x51BFA86A7FAF4EEA, MAAV102 }, // V1.04.01 43 | { 0x0582733413943655, MAAV102 }, // V1.04.03 44 | { 0x61F73244FA73079F, MAAV102 }, // V1.04.05 core/v145 45 | { 0xAD80988AE986097B, MAAV105 }, // contains two firmwares: 46 | // CORE by HWP (V1.04.05), the original firmware, programmable by keyboard controls 47 | // CORE by MPC (V1.04.06), the new firmware, programmable by web UI-generated keymaps (http://www.vortexgear.tw/mpc/index.html) 48 | 49 | // Vortex CORE RGB (product 293, pid 175?) 50 | { 0xA85878CBD05591A1, MAAV102 }, // V1.04.06 51 | 52 | // Vortex RACE3 (New 75) (192) 53 | { 0xB542D0D86B9A85C3, MAAV102 }, // V1.02.01 54 | { 0xFBF40BEE5D0A3C70, MAAV102 }, // V1.02.04 race/v124 55 | { 0xAD8B210C77D9D90F, MAAV102 }, // V1.02.05 56 | 57 | // Vortex Cypher (282) 58 | { 0xC259BB38A57783D, MAAV102 }, // V1.03.06 cypher/v136 59 | 60 | // POK3R RGB V2 (207) 61 | { 0x8AA1AEA217DA685B, MAAV102 }, // V1.00.05 pok3r_rgb2/v105 62 | 63 | // Vortex ViBE (216) 64 | { 0xCE7C8EAA3D28B10D, MAAV102 }, // V1.01.03 vibe/v113 65 | 66 | // Vortex Tab 60 (304) 67 | { 0xF5ED2438D4445703, MAAV102 }, // V1.01.13 tab60/v1113 68 | 69 | // Vortex Tab 75 (344) 70 | { 0x4399C7232F89BBDD, MAAV105 }, // V1.00.04 tab75/v104 71 | 72 | // Vortex Tab 90 (346) 73 | { 0xBFCCB61A61996BB3, MAAV105 }, // V1.00.04 tab90/v104 74 | 75 | // KBP V60 (112) 76 | { 0x6064D8C4EE74BE18, KBPV60 }, // V1.0.7 kbpv60 77 | 78 | // KBP V80 (129) 79 | { 0xBCF4C9830D800D8C, KBPV80 }, // V1.0.7 kbpv80 80 | 81 | // TEX Yoda II (163) 82 | { 0xF5A3714FA9A3CA40, MAAV102 }, // V1.01.01 83 | 84 | // Mistel Barocco MD600 (143) 85 | { 0xFA5DF5F231700316, MAAV102 }, // V1.04.08 md600/v148 86 | 87 | // Mistel Freeboard MD200 (200) 88 | // Vortex Tester (200) 89 | { 0x58B42FF4B1C57C09, MAAV102 }, // V1.01.02 md200/v112 90 | 91 | // Cooler Master Masterkeys Pro L White 92 | { 0x38cc849b2e54b6df, MAAV102 }, // V1.08.00 cmprolwhite/v180 93 | 94 | // Cooler Master Masterkeys Pro M White 95 | { 0x12fbf4668bdfe188, MAAV102 }, // V1.06.00 96 | 97 | // Cooler Master Masterkeys Pro S RGB 98 | { 0x91d591ac1a77b2d, MAAV101 }, // 1.2.1 cmprosrgb/v121 99 | { 0x836c83cc7d4e9f1, MAAV101 }, // 1.2.2 cmprosrgb/v122 100 | 101 | // Cooler Master Masterkeys Pro M RGB 102 | { 0xfdf7ac5b93d67ead, MAAV102 }, // V1.04.00 103 | { 0x2f69c079f9d53765, MAAV102 }, // V1.04.01 104 | 105 | // Cooler Master Masterkeys Pro L RGB 106 | { 0x57ca9d8e07d0c95a, MAAV101 }, // 1.2.1 107 | { 0x2adc9b96d5cf26c7, MAAV101 }, // 1.2.2 108 | }; 109 | 110 | const ZMap types = { 111 | { MAAJONSN, decode_maajonsn }, 112 | { MAAV101, decode_maav101 }, 113 | { MAAV102, decode_maav102 }, 114 | { KBPV60, decode_kbp_v60 }, 115 | { KBPV80, decode_kbp_v80 }, 116 | { MAAV105, decode_maav105 }, 117 | }; 118 | 119 | UpdatePackage::UpdatePackage(){ 120 | 121 | } 122 | 123 | bool UpdatePackage::loadFromExe(ZPath exe, int index){ 124 | LOG("Extract from " << exe); 125 | ZFile file; 126 | if(!file.open(exe, ZFile::READ)){ 127 | ELOG("Failed to open file"); 128 | return false; 129 | } 130 | 131 | zu64 exehash = ZFile::fileHash(exe); 132 | if(packages.contains(exehash)){ 133 | int ret = types[packages[exehash]](&file, firmware); 134 | return !ret; 135 | } else { 136 | ELOG("Unknown updater executable: " << ZString::ItoS(exehash, 16)); 137 | return false; 138 | } 139 | 140 | return true; 141 | } 142 | 143 | const ZBinary &UpdatePackage::getFirmware() const { 144 | return firmware; 145 | } 146 | 147 | /* Decode the encryption scheme used by the updater program. 148 | * Produced from IDA disassembly in sub_401000 of v117 updater. 149 | * First, swap the 1st and 4th bytes, every 5 bytes 150 | * Second, reverse each pair of bytes 151 | * Third, shift the bits in each byte, sub 7 from MSBs 152 | */ 153 | void decode_package_data(ZBinary &bin){ 154 | // Swap bytes 4 apart, skip 5 155 | for(zu64 i = 4; i < bin.size(); i+=5){ 156 | zbyte a = bin[i-4]; 157 | zbyte b = bin[i]; 158 | bin[i-4] = b; 159 | bin[i] = a; 160 | } 161 | 162 | // Swap bytes in each set of two bytes 163 | for(zu64 i = 1; i < bin.size(); i+=2){ 164 | zbyte d = bin[i-1]; 165 | zbyte b = bin[i]; 166 | bin[i-1] = b; 167 | bin[i] = d; 168 | } 169 | 170 | // y = ((x - 7) << 4) + (x >> 4) 171 | for(zu64 i = 0; i < bin.size(); ++i){ 172 | bin[i] = (((bin[i] - 7) << 4) + (bin[i] >> 4)) & 0xFF; 173 | } 174 | } 175 | 176 | /* Encrypt using the encryption scheme used by the updater program 177 | * Reverse engineered from the above 178 | */ 179 | void encode_package_data(ZBinary &bin){ 180 | // x = (y >> 4 + 7 & 0xF) | (x << 4) 181 | for(zu64 i = 0; i < bin.size(); ++i){ 182 | bin[i] = ((((bin[i] >> 4) + 7) & 0xF) | (bin[i] << 4)) & 0xFF; 183 | } 184 | 185 | // Swap bytes in each set of two bytes 186 | for(zu64 i = 1; i < bin.size(); i+=2){ 187 | zbyte d = bin[i-1]; 188 | zbyte b = bin[i]; 189 | bin[i-1] = b; 190 | bin[i] = d; 191 | } 192 | 193 | // Swap bytes 4 apart, skip 5 194 | for(zu64 i = 4; i < bin.size(); i+=5){ 195 | zbyte a = bin[i-4]; 196 | zbyte b = bin[i]; 197 | bin[i-4] = b; 198 | bin[i] = a; 199 | } 200 | } 201 | 202 | ZBinary pkg_file_read(ZFile *file, zu64 pos, zu64 len){ 203 | ZBinary data; 204 | if(file->seek(pos) != pos) 205 | throw ZException("File too short - seek"); 206 | if(file->read(data, len) != len) 207 | throw ZException("File too short - read"); 208 | return data; 209 | } 210 | 211 | /* Decode the updater for the POK3R. 212 | */ 213 | int decode_maajonsn(ZFile *file, ZBinary &fw_out){ 214 | zu64 exelen = file->fileSize(); 215 | 216 | zu64 strings_len = 0x4B8; 217 | 218 | zu64 offset_company = 0x10; 219 | zu64 offset_product = offset_company + 0x208; // 0x218 220 | zu64 offset_version = 0x460; 221 | zu64 offset_sig = 0x4AE; 222 | 223 | zu64 strings_start = exelen - strings_len; 224 | 225 | // Read strings 226 | ZBinary strs = pkg_file_read(file, strings_start, strings_len); 227 | // Decrypt strings 228 | decode_package_data(strs); 229 | 230 | ZString company; 231 | ZString product; 232 | ZString version; 233 | 234 | // Company name 235 | company.parseUTF16((const zu16 *)(strs.raw() + offset_company), 0x200); 236 | // Product name 237 | product.parseUTF16((const zu16 *)(strs.raw() + offset_product), 0x200); 238 | // Version 239 | version = ZString(strs.raw() + offset_version, 12); 240 | 241 | LOG("Company: " << company); 242 | LOG("Product: " << product); 243 | LOG("Version: " << version); 244 | 245 | LOG("Signature: " << ZString(strs.raw() + offset_sig, strings_len - offset_sig)); 246 | 247 | // LOG("String Dump:"); 248 | // RLOG(strs.dumpBytes(4, 8)); 249 | 250 | // Decode other encrypted sections 251 | 252 | zu64 total = strings_len; 253 | ZArray sections; 254 | 255 | zu64 sec_len = ZBinary::decleu32(strs.raw() + 0x420); // Firmware length 256 | 257 | ZString layout; 258 | layout.parseUTF16((const zu16 *)(strs.raw() + 0x424), 0x20); 259 | LOG("Layout: " << layout); 260 | 261 | total += sec_len; 262 | sections.push(sec_len); 263 | 264 | zu64 sec_start = exelen - total; 265 | 266 | LOG("Offset: 0x" << ZString::ItoS(sec_start, 16)); 267 | LOG("Length: 0x" << ZString::ItoS(sec_len, 16)); 268 | 269 | // Read section 270 | ZBinary sec = pkg_file_read(file, sec_start, sec_len); 271 | // Decode section 272 | decode_package_data(sec); 273 | 274 | // Decrypt firmware 275 | ProtoPOK3R::decode_firmware(sec); 276 | 277 | // LOG("Section Dump:"); 278 | // RLOG(sec.dumpBytes(4, 8, 0)); 279 | 280 | // Write firmware 281 | fw_out = sec; 282 | 283 | return 0; 284 | 285 | } 286 | 287 | /* Decode the updater for the CM MK Pro S/L RGB 288 | */ 289 | int decode_maav101(ZFile *file, ZBinary &fw_out){ 290 | zu64 exelen = file->fileSize(); 291 | 292 | zu64 strings_len = 0x4BC; 293 | 294 | zu64 offset_company = 0x10; 295 | zu64 offset_product = 0x218; 296 | zu64 offset_version = 0x461; 297 | zu64 offset_sig = 0x4AF; 298 | 299 | zu64 strings_start = exelen - strings_len; 300 | 301 | // Read strings 302 | ZBinary strs = pkg_file_read(file, strings_start, strings_len); 303 | // Decrypt strings 304 | decode_package_data(strs); 305 | 306 | ZString company; 307 | ZString product; 308 | ZString version; 309 | 310 | // Company name 311 | company.parseUTF16((const zu16 *)(strs.raw() + offset_company), 0x200); 312 | // Product name 313 | product.parseUTF16((const zu16 *)(strs.raw() + offset_product), 0x200); 314 | // Version 315 | version = ZString(strs.raw() + offset_version, 12); 316 | 317 | LOG("Company: " << company); 318 | LOG("Product: " << product); 319 | LOG("Version: " << version); 320 | 321 | LOG("Signature: " << ZString(strs.raw() + offset_sig, strings_len - offset_sig)); 322 | 323 | // LOG("String Dump:"); 324 | // RLOG(strs.dumpBytes(4, 8)); 325 | 326 | // Decode other encrypted sections 327 | 328 | zu64 total = strings_len; 329 | ZArray sections; 330 | 331 | zu64 sec_len = ZBinary::decleu32(strs.raw() + 0x420); // Firmware length 332 | 333 | ZString layout; 334 | layout.parseUTF16((const zu16 *)(strs.raw() + 0x424), 0x20); 335 | LOG("Layout: " << layout); 336 | 337 | total += sec_len; 338 | sections.push(sec_len); 339 | 340 | zu64 sec_start = exelen - total; 341 | 342 | LOG("Offset: 0x" << ZString::ItoS(sec_start, 16)); 343 | LOG("Length: 0x" << ZString::ItoS(sec_len, 16)); 344 | 345 | // Read section 346 | ZBinary sec = pkg_file_read(file, sec_start, sec_len); 347 | // Decode section 348 | decode_package_data(sec); 349 | 350 | // Decrypt firmware 351 | ProtoPOK3R::decode_firmware(sec); 352 | 353 | // LOG("Section Dump:"); 354 | // RLOG(sec.dumpBytes(4, 8, 0)); 355 | 356 | // Write firmware 357 | fw_out = sec; 358 | 359 | return 0; 360 | 361 | } 362 | 363 | /* Decode the updater for the POK3R RGB / Vortex Core. 364 | */ 365 | int decode_maav102(ZFile *file, ZBinary &fw_out){ 366 | zu64 exelen = file->fileSize(); 367 | 368 | zu64 strings_len = 0xB24; // from IDA disassembly in sub_403830 of v130 updater 369 | // same size in v104 370 | zu64 strings_start = exelen - strings_len; 371 | 372 | zu64 offset_desc = 0x26; 373 | zu64 offset_company = offset_desc + 0x208; // 0x22e 374 | zu64 offset_product = offset_company + 0x208; // 0x436 375 | zu64 offset_version = offset_product + 0x208; // 0x63e 376 | zu64 offset_sig = 0xb19; 377 | 378 | // Read strings 379 | ZBinary strs = pkg_file_read(file, strings_start, strings_len); 380 | // Decrypt strings 381 | decode_package_data(strs); 382 | 383 | ZString desc; 384 | ZString company; 385 | ZString product; 386 | ZString version; 387 | 388 | // Description 389 | desc.parseUTF16((const zu16 *)(strs.raw() + offset_desc), 0x200); 390 | // Company name 391 | company.parseUTF16((const zu16 *)(strs.raw() + offset_company), 0x200); 392 | // Product name 393 | product.parseUTF16((const zu16 *)(strs.raw() + offset_product), 0x200); 394 | // Version 395 | version.parseUTF16((const zu16 *)(strs.raw() + offset_version), 0x200); 396 | 397 | LOG("=============================="); 398 | 399 | LOG("Description: " << desc); 400 | LOG("Company: " << company); 401 | LOG("Product: " << product); 402 | LOG("Version: " << version); 403 | 404 | LOG("Signature: " << ZString(strs.raw() + offset_sig, strings_len - offset_sig)); 405 | 406 | // LOG("String Dump:"); 407 | // RLOG(strs.dumpBytes(4, 8)); 408 | 409 | LOG("=============================="); 410 | 411 | // Decode other encrypted sections 412 | 413 | zu64 total = strings_len; 414 | ZArray sections; 415 | 416 | zu64 start = 0xAC8 - (0x50 * 8); 417 | for(zu8 i = 0; i < 8; ++i){ 418 | zu32 fwl = ZBinary::decleu32(strs.raw() + start); // Firmware length 419 | zu32 strl = ZBinary::decleu32(strs.raw() + start + 4); // Info length 420 | 421 | if(fwl){ 422 | ZString layout; 423 | layout.parseUTF16((const zu16 *)(strs.raw() + start + 8), 0x20); 424 | LOG("Layout " << i << ": " << layout); 425 | 426 | total += fwl; 427 | total += strl; 428 | sections.push(fwl); 429 | sections.push(strl); 430 | } 431 | start += 0x50; 432 | } 433 | 434 | LOG("=============================="); 435 | 436 | zu64 sec_start = exelen - total; 437 | LOG("Section Count: " << sections.size()); 438 | 439 | for(zu64 i = 0; i < sections.size(); ++i){ 440 | zu64 sec_len = sections[i]; 441 | if(sec_len == 0) 442 | continue; 443 | 444 | LOG("Section " << i << ":"); 445 | // LOG(" Offset: 0x" << ZString::ItoS(sec_start, 16)); 446 | LOG(" Length: " << sec_len); 447 | 448 | // Read section 449 | ZBinary sec = pkg_file_read(file, sec_start, sec_len); 450 | sec_start += sec_len; 451 | 452 | // Decode section 453 | decode_package_data(sec); 454 | 455 | // Decrypt RGB firmwares only 456 | if(sec.size() == 180){ 457 | LOG(" Data"); 458 | RLOG(sec.dumpBytes(4, 8, 0)); 459 | ProtoCYKB::info_section(sec); 460 | continue; 461 | } else { 462 | LOG(" Firmware" << ZLog::NOLN); 463 | ProtoCYKB::decode_firmware(sec); 464 | if(i == 0){ 465 | fw_out = sec; 466 | RLOG(" (output)"); 467 | } 468 | RLOG(ZLog::NEWLN); 469 | } 470 | } 471 | 472 | return 0; 473 | } 474 | 475 | int decode_maav105(ZFile *file, ZBinary &fw_out){ 476 | zu64 exelen = file->fileSize(); 477 | zu64 strings_len = 0x2b58; // from decompiled FUN_4049d0 of TAB_75_V100 478 | zu64 strings_start = exelen - strings_len; 479 | 480 | // zu64 offset_desc = 0x23de - 180; 481 | zu64 offset_desc = 0x232a; 482 | zu64 offset_company = offset_desc + 0x208; 483 | zu64 offset_product = offset_company + 0x208; 484 | zu64 offset_version = offset_product + 0x208; 485 | zu64 offset_sig = exelen - strings_start - 13; 486 | 487 | // Read strings 488 | ZBinary strs = pkg_file_read(file, strings_start, exelen - strings_start); 489 | // Decrypt strings 490 | decode_package_data(strs); 491 | 492 | // zu64 pos = 0x0; 493 | // for(int i = 0; i < 20; ++i){ 494 | // zu32 d = ZBinary::decleu32(strs.raw() + pos); // Firmware length 495 | // LOG(ZString::ItoS((zu64)d, 16)); 496 | // pos += 4; 497 | // } 498 | 499 | RLOG(strs.dumpBytes(4, 8)); 500 | ZFile::writeBinary("manifest.bin", strs); 501 | 502 | ZString pkgdesc; 503 | ZString company; 504 | ZString product; 505 | ZString pkgver; 506 | 507 | // Description 508 | pkgdesc.parseUTF16((const zu16 *)(strs.raw() + offset_desc), 0x200); 509 | // Company name 510 | company.parseUTF16((const zu16 *)(strs.raw() + offset_company), 0x200); 511 | // Product name 512 | product.parseUTF16((const zu16 *)(strs.raw() + offset_product), 0x200); 513 | // Version 514 | pkgver.parseUTF16((const zu16 *)(strs.raw() + offset_version), 0x200); 515 | 516 | LOG("=============================="); 517 | LOG("Description: " << pkgdesc); 518 | LOG("Company: " << company); 519 | LOG("Product: " << product); 520 | LOG("Version: " << pkgver); 521 | LOG("Signature: " << ZString(strs.raw() + offset_sig, 13)); 522 | 523 | zu64 section_start = 0x1F1600; 524 | 525 | zu64 list_pos = 0xc8; 526 | for(int i = 0; i < 4; ++i){ 527 | zu64 desc_start = list_pos; 528 | zu64 version_start = desc_start + 0x208; 529 | zu64 addr_pos = version_start + 0x208; 530 | zu64 layout_start = addr_pos + 8; 531 | 532 | LOG("=============================="); 533 | 534 | // Product name 535 | ZString desc; 536 | desc.parseUTF16((const zu16 *)(strs.raw() + desc_start), 0x200); 537 | // Version 538 | ZString version; 539 | version.parseUTF16((const zu16 *)(strs.raw() + version_start), 0x200); 540 | 541 | LOG("Description: " << desc); 542 | LOG("Version: " << version); 543 | 544 | list_pos = layout_start + 0x2c8; 545 | 546 | while(strs[layout_start]){ 547 | // Layout 548 | ZString layout; 549 | layout.parseUTF16((const zu16 *)(strs.raw() + layout_start), 0x200); 550 | zu16 a1 = ZBinary::decleu16(strs.raw() + layout_start + 60); 551 | zu16 a2 = ZBinary::decleu16(strs.raw() + layout_start + 62); 552 | zu16 a3 = ZBinary::decleu16(strs.raw() + layout_start + 64); 553 | LOG("Layout: " << layout << " " << a1 << " " << a2 << " " << a3); 554 | layout_start += 80; 555 | } 556 | 557 | zu32 fwl = ZBinary::decleu32(strs.raw() + addr_pos); // Firmware length 558 | zu32 strl = ZBinary::decleu32(strs.raw() + addr_pos + 4); // Info length 559 | 560 | // Read firmware 561 | ZBinary fw = pkg_file_read(file, section_start, fwl); 562 | section_start += fwl; 563 | // Decrypt firmware 564 | decode_package_data(fw); 565 | ProtoCYKB::decode_firmware(fw); 566 | 567 | // Read info 568 | ZBinary info = pkg_file_read(file, section_start, strl); 569 | section_start += strl; 570 | // Decrypt info 571 | decode_package_data(info); 572 | 573 | if(fw.size()) 574 | fw_out = fw; 575 | 576 | LOG("Firmware: " << fwl); 577 | // RLOG(fw.dumpBytes(4, 8)); 578 | LOG("Info: " << strl); 579 | if(info.size()) 580 | RLOG(info.dumpBytes(4, 8)); 581 | 582 | } 583 | 584 | return 0; 585 | } 586 | 587 | void kbp_decrypt(zbyte *data, zu64 size, zu32 key){ 588 | zbyte xor_key[4]; 589 | ZBinary::encbeu32(xor_key, key); 590 | for(zu64 i = 0; i < size; ++i){ 591 | data[i] = data[i] ^ xor_key[i % 4] ^ (i & 0xFF); 592 | } 593 | } 594 | 595 | /* Decode the updater for the KBP V60 / V80. 596 | */ 597 | int decode_kbp_cykb(ZFile *file, ZBinary &fw_out, zu32 key){ 598 | zu64 exelen = file->fileSize(); 599 | zu64 strings_len = 588; 600 | zu64 strings_start = exelen - strings_len; 601 | 602 | // Read strings 603 | ZBinary strs; 604 | if(file->seek(strings_start) != strings_start){ 605 | LOG("File too short - seek"); 606 | return -4; 607 | } 608 | if(file->read(strs, strings_len) != strings_len){ 609 | LOG("File too short - read"); 610 | return -5; 611 | } 612 | 613 | // Decrypt strings 614 | kbp_decrypt(strs.raw(), strs.size(), key); 615 | 616 | LOG("String Dump:"); 617 | RLOG(strs.dumpBytes(4, 8)); 618 | 619 | zu64 fw_start = 0x54000; 620 | zu64 fw_len = ZBinary::decleu32(strs.raw() + 4); 621 | 622 | LOG("Firmware Size 0x" << ZString::ItoS(fw_len, 16)); 623 | 624 | // Read firmware 625 | ZBinary fw; 626 | if(file->seek(fw_start) != fw_start){ 627 | LOG("File too short - seek"); 628 | return -2; 629 | } 630 | if(file->read(fw, fw_len) != fw_len){ 631 | LOG("File too short - read"); 632 | return -3; 633 | } 634 | 635 | // Decrypt firmware 636 | kbp_decrypt(fw.raw(), fw.size(), key); 637 | ProtoPOK3R::decode_firmware(fw); 638 | fw_out = fw; 639 | 640 | //RLOG(fw_out.dumpBytes(4, 8)); 641 | 642 | return 0; 643 | } 644 | 645 | int decode_kbp_v60(ZFile *file, ZBinary &fw_out){ 646 | zu32 key = 0xDA6282CD; // v60 647 | return decode_kbp_cykb(file, fw_out, key); 648 | } 649 | 650 | int decode_kbp_v80(ZFile *file, ZBinary &fw_out){ 651 | zu32 key = 0xF6F3111F; // v80 652 | return decode_kbp_cykb(file, fw_out, key); 653 | } 654 | 655 | int encode_image(ZPath fwin, ZPath fwout){ 656 | LOG("Input: " << fwin); 657 | 658 | // Read firmware 659 | ZBinary fwbin; 660 | if(!ZFile::readBinary(fwin, fwbin)){ 661 | LOG("Failed to read file"); 662 | return -1; 663 | } 664 | 665 | // Pok3r::encode_firmware(fwbin); 666 | ProtoCYKB::encode_firmware(fwbin); 667 | encode_package_data(fwbin); 668 | 669 | LOG("Output: " << fwout); 670 | 671 | // Write encoded image 672 | if(!ZFile::writeBinary(fwout, fwbin)){ 673 | ELOG("Failed to write file"); 674 | return -2; 675 | } 676 | 677 | return 0; 678 | } 679 | 680 | int encode_patch_updater(ZPath exein, ZPath fwin, ZPath exeout){ 681 | LOG("In Updater: " << exein); 682 | LOG("In Firmware: " << fwin); 683 | 684 | // Read updater 685 | ZBinary exebin; 686 | if(!ZFile::readBinary(exein, exebin)){ 687 | ELOG("Failed to read file"); 688 | return -1; 689 | } 690 | 691 | // Read firmware 692 | ZBinary fwbin; 693 | if(!ZFile::readBinary(fwin, fwbin)){ 694 | LOG("Failed to read file"); 695 | return -2; 696 | } 697 | 698 | // Encode firmware 699 | // Pok3r::encode_firmware(fwbin); 700 | ProtoCYKB::encode_firmware(fwbin); 701 | encode_package_data(fwbin); 702 | 703 | // Write encoded firmware onto exe 704 | // exebin.seek(0x1A3800); 705 | exebin.seek(0x2BE000); 706 | exebin.write(fwbin); 707 | 708 | ZFile exefile; 709 | if(!exefile.open(exeout, ZFile::WRITE)){ 710 | ELOG("Failed to open file"); 711 | return -3; 712 | } 713 | // Write updated exe 714 | if(!exefile.write(exebin)){ 715 | LOG("Write error"); 716 | return -4; 717 | } 718 | 719 | LOG("Out Updater: " << exeout); 720 | 721 | return 0; 722 | } 723 | --------------------------------------------------------------------------------