From 28203e909e83b1ac6becb45a3eadae23b190df32 Mon Sep 17 00:00:00 2001 From: tmk Date: Thu, 11 Feb 2016 12:21:21 +0900 Subject: [PATCH] Squashed 'tmk_core/' changes from 57d27a8..08ce4c3 08ce4c3 Add update for chibios in README 30cac1d Merge remote-tracking branch 'flabbergast-tmk_keyboard/chibios' f218a38 core: Remove unused warning. Fix tmk_keyboard/#293 3e68807 Chibios: add more guards for transmitting (fix a deadlock bug). cecc807 core: Speed up compilation d01d959 Merge remote-tracking branch 'upstream/master' into chibios 8ff12ae Merge pull request #230 from jerryen/master 3afb83d core: Add note about sudo and git 2525d5d Chibios: Update to new USB API. 2fc5cd6 Merge branch 'master' into chibios 6f5511a core: Fix variable initialize 02a15fa core: Fix variable init and header include 1833ca1 Merge pull request #292 from obones/command_warning bc9dc6f Merge pull request #294 from shayneholmes/fix-debug-message c1b891b Merge pull request #295 from doopai/master 1da837c core: Add macro commands SM(), RM() and CM() d9d5a93 Add support for storing, restoring and clearing modifiers in macros 4f371c0 core: Add dfu-programmer example to doc/build.md ba3792e core: Fix doc/build.md 4b99fed remove SERIAL_SOFT_DEBUG macro 984accb Fix typo in bootmagic debug message 67a7ea1 usb_usb: Fix for keymap editor 2fc9ae4 host_driver is only used if KEYBOARD_LOCK_ENABLE is defined a35cdea Chibios: add 'core/protocol' to the makefiles' search path. 5ed2fd6 Merge remote-tracking branch 'upstream/master' into chibios bbc4851 Do timeout when writing to CONSOLE EP queue. 2450559 Remove an errant comma trailing the KC_ERAS macro alias 8465747 Merge pull request #270 from jeffgran/single-tap-toggle a37339a Merge pull request #271 from jeffgran/default-layer-toggle 8e732f7 core: Update doc/build.md and remove other_projects.md e47dc15 Workaround for compiler warnings when console disabled. 4184c52 Add maple mini code. b555238 core: Fix rules.mk for dfu target 26dacf4 add ACTION_DEFAULT_LAYER_TOGGLE to toggle a default layer on/off 83607d9 enable TAPPING_TOGGLE=1 to work correctly bef3089 Merge branch 'master' into chibios f7a55fd core: Fix debug print of usb_hid 1402f7d core: Add keycode KC_JPY for JIS 8e3a460 Chibios: Remove the wait in the main loop. 7d41746 ChibiOS: prettify/document sleep_led code. 235cdee ChibiOS: Update infinity bootloader code to match updated ChibiOS. 807bf1e Chibios: Add breathing sleep LED on Kinetis MCUs. ba3bf7c ChibiOS: make reset request more CMSISy. bf2ffd4 Add correct chibios/bootloader_jump for infinity KB. f5e03fd Chibios/usb_main: rename a variable for clarity. 63a330c Fix drop key stroke d8f5b06 Fix report size of boot protocol. 38abb50 ChibiOS/STM32: send remote wakeup. 6725650 ChibiOS/kinetis: sending remote wakeup. 69f6e54 Chibios: use WFI in idle. WIP suspend stuff. 02f044b Move chibios/cortex selection to local Makefiles. fa59dfa Rename chibios example keyboard. 5c060de Add eeprom support for chibios/kinetis. efd0044 Move declaration of keymap_config. 7196b24 Make bootmagic.c code portable (_delay_ms -> wait_ms). 37b15b2 Add missing #include to keymap.c. 31316e3 Merge branch 'master' into chibios 166f312 Fix chibios/usb_main GET_REPORT handing. 57ac6c2 Add ARM Teensies bootloader code. 1758086 Move STM32 bootloader address config to separate .h file. 03bb026 Rename some Makefile defines. 76ba6ac NKRO fixes. 06498f8 Update chibios README. db0a4f5 Add basic sleep_led for chibios. c3f930e Move AVR specific sleep_led.c into avr. 4069494 Fix bootloader-jump compiling. 9117f7b Small updates. 269cb85 Implement jump-to-bootloader. 7e81b34 Move chibios to tool. 923a892 Make usb_main more USB_DRIVER #define independent. aa054f0 Add ChibiOS support (USB stack + support files). 1e3e282 Modularity and gcc warnings fixes. 2f60ce0 Add KEYBOARD_LOCK_ENABLE to makefile git-subtree-dir: tmk_core git-subtree-split: 08ce4c3021d2ecd446c5b9a137aded65423d241e --- README.md | 16 +- common.mk | 11 +- common/action.c | 9 +- common/action_code.h | 1 + common/action_macro.c | 13 + common/action_macro.h | 12 + common/action_tapping.c | 11 +- common/{ => avr}/sleep_led.c | 0 common/bootmagic.c | 7 +- common/chibios/bootloader.c | 46 + common/chibios/eeconfig.c | 551 ++++++++++ common/chibios/printf.c | 240 +++++ common/chibios/printf.h | 111 ++ common/chibios/sleep_led.c | 164 +++ common/chibios/suspend.c | 65 ++ common/chibios/timer.c | 27 + common/command.c | 28 +- common/keycode.h | 11 +- common/keymap.c | 12 +- common/keymap.h | 1 - common/nodebug.h | 2 + common/print.c | 8 +- common/print.h | 17 +- common/progmem.h | 4 +- common/report.h | 5 + common/wait.h | 8 +- doc/build.md | 64 +- doc/keymap.md | 24 +- doc/other_projects.md | 62 -- protocol/chibios/README.md | 50 + protocol/chibios/main.c | 139 +++ protocol/chibios/usb_main.c | 1372 +++++++++++++++++++++++++ protocol/chibios/usb_main.h | 143 +++ protocol/serial_soft.c | 3 +- protocol/usb_hid/parser.cpp | 2 +- rules.mk | 1 - tool/chibios/.gitignore | 1 + tool/chibios/ch-bootloader-jump.patch | 116 +++ tool/chibios/chibios.mk | 231 +++++ tool/chibios/common.mk | 85 ++ tool/mbed/common.mk | 2 +- 41 files changed, 3534 insertions(+), 141 deletions(-) rename common/{ => avr}/sleep_led.c (100%) create mode 100644 common/chibios/bootloader.c create mode 100644 common/chibios/eeconfig.c create mode 100644 common/chibios/printf.c create mode 100644 common/chibios/printf.h create mode 100644 common/chibios/sleep_led.c create mode 100644 common/chibios/suspend.c create mode 100644 common/chibios/timer.c delete mode 100644 doc/other_projects.md create mode 100644 protocol/chibios/README.md create mode 100644 protocol/chibios/main.c create mode 100644 protocol/chibios/usb_main.c create mode 100644 protocol/chibios/usb_main.h create mode 100644 tool/chibios/.gitignore create mode 100644 tool/chibios/ch-bootloader-jump.patch create mode 100644 tool/chibios/chibios.mk create mode 100644 tool/chibios/common.mk diff --git a/README.md b/README.md index 6b6714a6..f460d0ed 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,16 @@ This is a keyboard firmware library with some useful features for Atmel AVR and Source code is available here: +Updates +------- +#### 2016/02/10 +flabbergast's Chibios protocol was merged from . See [protocol/chibios/README.md](protocol/chibios/README.md). Chibios protocol supports Cortex-M such as STM32 and Kinetis. + +#### 2015/04/22 +separated with TMK Keyboard Firmware Collection + + + Features -------- These features can be used in your keyboard. @@ -27,12 +37,6 @@ These features can be used in your keyboard. -Updates -------- -2015/04/22 separated with TMK Keyboard Firmware Collection - - - TMK Keyboard Firmware Collection -------------------------------- Complete firmwares for various keyboards and protocol converters. diff --git a/common.mk b/common.mk index cb030b33..ebaafd1d 100644 --- a/common.mk +++ b/common.mk @@ -1,4 +1,4 @@ -COMMON_DIR = common +COMMON_DIR = $(TMK_DIR)/common SRC += $(COMMON_DIR)/host.c \ $(COMMON_DIR)/keyboard.c \ $(COMMON_DIR)/action.c \ @@ -53,8 +53,12 @@ ifdef USB_6KRO_ENABLE OPT_DEFS += -DUSB_6KRO_ENABLE endif +ifdef KEYBOARD_LOCK_ENABLE + OPT_DEFS += -DKEYBOARD_LOCK_ENABLE +endif + ifdef SLEEP_LED_ENABLE - SRC += $(COMMON_DIR)/sleep_led.c + SRC += $(COMMON_DIR)/avr/sleep_led.c OPT_DEFS += -DSLEEP_LED_ENABLE OPT_DEFS += -DNO_SUSPEND_POWER_DOWN endif @@ -77,7 +81,8 @@ ifdef KEYMAP_SECTION_ENABLE endif # Version string -OPT_DEFS += -DVERSION=$(shell (git describe --always --dirty || echo 'unknown') 2> /dev/null) +VERSION := $(shell (git describe --always --dirty || echo 'unknown') 2> /dev/null) +OPT_DEFS += -DVERSION=$(VERSION) # Search Path diff --git a/common/action.c b/common/action.c index 1f15bd09..339b3ee6 100644 --- a/common/action.c +++ b/common/action.c @@ -131,10 +131,17 @@ void process_action(keyrecord_t *record) case MODS_TAP_TOGGLE: if (event.pressed) { if (tap_count <= TAPPING_TOGGLE) { - register_mods(mods); + if (mods & get_mods()) { + dprint("MODS_TAP_TOGGLE: toggle mods off\n"); + unregister_mods(mods); + } else { + dprint("MODS_TAP_TOGGLE: toggle mods on\n"); + register_mods(mods); + } } } else { if (tap_count < TAPPING_TOGGLE) { + dprint("MODS_TAP_TOGGLE: release : unregister_mods\n"); unregister_mods(mods); } } diff --git a/common/action_code.h b/common/action_code.h index 4fe9c1d5..da93f77b 100644 --- a/common/action_code.h +++ b/common/action_code.h @@ -255,6 +255,7 @@ enum layer_pram_tap_op { #define ACTION_LAYER_TAP(layer, key) (ACT_LAYER_TAP<<12 | (layer)<<8 | (key)) /* Default Layer */ #define ACTION_DEFAULT_LAYER_SET(layer) ACTION_DEFAULT_LAYER_BIT_SET((layer)/4, 1<<((layer)%4)) +#define ACTION_DEFAULT_LAYER_TOGGLE(layer) ACTION_DEFAULT_LAYER_BIT_XOR((layer)/4, 1<<((layer)%4)) /* Layer Operation */ #define ACTION_LAYER_CLEAR(on) ACTION_LAYER_BIT_AND(0, 0, (on)) #define ACTION_LAYER_MOMENTARY(layer) ACTION_LAYER_ON_OFF(layer) diff --git a/common/action_macro.c b/common/action_macro.c index ffaf125c..b2891bf4 100644 --- a/common/action_macro.c +++ b/common/action_macro.c @@ -34,6 +34,8 @@ void action_macro_play(const macro_t *macro_p) macro_t macro = END; uint8_t interval = 0; + uint8_t mod_storage = 0; + if (!macro_p) return; while (true) { switch (MACRO_READ()) { @@ -66,6 +68,17 @@ void action_macro_play(const macro_t *macro_p) interval = MACRO_READ(); dprintf("INTERVAL(%u)\n", interval); break; + case MOD_STORE: + mod_storage = get_mods(); + break; + case MOD_RESTORE: + set_mods(mod_storage); + send_keyboard_report(); + break; + case MOD_CLEAR: + clear_mods(); + send_keyboard_report(); + break; case 0x04 ... 0x73: dprintf("DOWN(%02X)\n", macro); register_code(macro); diff --git a/common/action_macro.h b/common/action_macro.h index aedc32ec..4cf2216d 100644 --- a/common/action_macro.h +++ b/common/action_macro.h @@ -64,6 +64,9 @@ enum macro_command_id{ /* 0x74 - 0x83 */ WAIT = 0x74, INTERVAL, + MOD_STORE, + MOD_RESTORE, + MOD_CLEAR, /* 0x84 - 0xf3 (reserved for keycode up) */ @@ -82,6 +85,9 @@ enum macro_command_id{ #define TYPE(key) DOWN(key), UP(key) #define WAIT(ms) WAIT, (ms) #define INTERVAL(ms) INTERVAL, (ms) +#define STORE() MOD_STORE +#define RESTORE() MOD_RESTORE +#define CLEAR() MOD_CLEAR /* key down */ #define D(key) DOWN(KC_##key) @@ -93,6 +99,12 @@ enum macro_command_id{ #define W(ms) WAIT(ms) /* interval */ #define I(ms) INTERVAL(ms) +/* store modifier(s) */ +#define SM() STORE() +/* restore modifier(s) */ +#define RM() RESTORE() +/* clear modifier(s) */ +#define CM() CLEAR() /* for backward comaptibility */ #define MD(key) DOWN(KC_##key) diff --git a/common/action_tapping.c b/common/action_tapping.c index 826c2330..a3a5a7d0 100644 --- a/common/action_tapping.c +++ b/common/action_tapping.c @@ -30,7 +30,6 @@ static bool process_tapping(keyrecord_t *record); static bool waiting_buffer_enq(keyrecord_t record); static void waiting_buffer_clear(void); static bool waiting_buffer_typed(keyevent_t event); -static bool waiting_buffer_has_anykey_pressed(void); static void waiting_buffer_scan_tap(void); static void debug_tapping_key(void); static void debug_waiting_buffer(void); @@ -257,7 +256,7 @@ bool process_tapping(keyrecord_t *keyp) return true; } } else { - if (!IS_NOEVENT(event)) debug("Tapping: other key just after tap.\n"); + if (!IS_NOEVENT(event)) debug("Tapping: other key just after tap.\n") {}; process_action(keyp); return true; } @@ -324,14 +323,6 @@ bool waiting_buffer_typed(keyevent_t event) return false; } -bool waiting_buffer_has_anykey_pressed(void) -{ - for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) { - if (waiting_buffer[i].event.pressed) return true; - } - return false; -} - /* scan buffer for tapping */ void waiting_buffer_scan_tap(void) { diff --git a/common/sleep_led.c b/common/avr/sleep_led.c similarity index 100% rename from common/sleep_led.c rename to common/avr/sleep_led.c diff --git a/common/bootmagic.c b/common/bootmagic.c index b002a585..08241f7d 100644 --- a/common/bootmagic.c +++ b/common/bootmagic.c @@ -1,6 +1,6 @@ #include #include -#include +#include "wait.h" #include "matrix.h" #include "bootloader.h" #include "debug.h" @@ -10,6 +10,7 @@ #include "eeconfig.h" #include "bootmagic.h" +keymap_config_t keymap_config; void bootmagic(void) { @@ -19,9 +20,9 @@ void bootmagic(void) } /* do scans in case of bounce */ - print("boogmagic scan: ... "); + print("bootmagic scan: ... "); uint8_t scan = 100; - while (scan--) { matrix_scan(); _delay_ms(10); } + while (scan--) { matrix_scan(); wait_ms(10); } print("done.\n"); /* bootmagic skip */ diff --git a/common/chibios/bootloader.c b/common/chibios/bootloader.c new file mode 100644 index 00000000..c8888f3c --- /dev/null +++ b/common/chibios/bootloader.c @@ -0,0 +1,46 @@ +#include "bootloader.h" + +#include "ch.h" +#include "hal.h" + +#ifdef STM32_BOOTLOADER_ADDRESS +/* STM32 */ + +#if defined(STM32F0XX) +/* This code should be checked whether it runs correctly on platforms */ +#define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0)) +extern uint32_t __ram0_end__; + +void bootloader_jump(void) { + *((unsigned long *)(SYMVAL(__ram0_end__) - 4)) = 0xDEADBEEF; // set magic flag => reset handler will jump into boot loader + NVIC_SystemReset(); +} + +#else /* defined(STM32F0XX) */ +#error Check that the bootloader code works on your platform and add it to bootloader.c! +#endif /* defined(STM32F0XX) */ + +#elif defined(KL2x) || defined(K20x) /* STM32_BOOTLOADER_ADDRESS */ +/* Kinetis */ + +#if defined(KIIBOHD_BOOTLOADER) +/* Kiibohd Bootloader (MCHCK and Infinity KB) */ +#define SCB_AIRCR_VECTKEY_WRITEMAGIC 0x05FA0000 +const uint8_t sys_reset_to_loader_magic[] = "\xff\x00\x7fRESET TO LOADER\x7f\x00\xff"; +void bootloader_jump(void) { + __builtin_memcpy((void *)VBAT, (const void *)sys_reset_to_loader_magic, sizeof(sys_reset_to_loader_magic)); + // request reset + SCB->AIRCR = SCB_AIRCR_VECTKEY_WRITEMAGIC | SCB_AIRCR_SYSRESETREQ_Msk; +} + +#else /* defined(KIIBOHD_BOOTLOADER) */ +/* Default for Kinetis - expecting an ARM Teensy */ +void bootloader_jump(void) { + chThdSleepMilliseconds(100); + __BKPT(0); +} +#endif /* defined(KIIBOHD_BOOTLOADER) */ + +#else /* neither STM32 nor KINETIS */ +void bootloader_jump(void) {} +#endif \ No newline at end of file diff --git a/common/chibios/eeconfig.c b/common/chibios/eeconfig.c new file mode 100644 index 00000000..f9e2718a --- /dev/null +++ b/common/chibios/eeconfig.c @@ -0,0 +1,551 @@ +#include "ch.h" +#include "hal.h" + +#include "eeconfig.h" + +/*************************************/ +/* Hardware backend */ +/* */ +/* Code from PJRC/Teensyduino */ +/*************************************/ + +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2013 PJRC.COM, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#if defined(K20x) /* chip selection */ +/* Teensy 3.0, 3.1, 3.2; mchck; infinity keyboard */ + +// The EEPROM is really RAM with a hardware-based backup system to +// flash memory. Selecting a smaller size EEPROM allows more wear +// leveling, for higher write endurance. If you edit this file, +// set this to the smallest size your application can use. Also, +// due to Freescale's implementation, writing 16 or 32 bit words +// (aligned to 2 or 4 byte boundaries) has twice the endurance +// compared to writing 8 bit bytes. +// +#define EEPROM_SIZE 32 + +// Writing unaligned 16 or 32 bit data is handled automatically when +// this is defined, but at a cost of extra code size. Without this, +// any unaligned write will cause a hard fault exception! If you're +// absolutely sure all 16 and 32 bit writes will be aligned, you can +// remove the extra unnecessary code. +// +#define HANDLE_UNALIGNED_WRITES + +// Minimum EEPROM Endurance +// ------------------------ +#if (EEPROM_SIZE == 2048) // 35000 writes/byte or 70000 writes/word + #define EEESIZE 0x33 +#elif (EEPROM_SIZE == 1024) // 75000 writes/byte or 150000 writes/word + #define EEESIZE 0x34 +#elif (EEPROM_SIZE == 512) // 155000 writes/byte or 310000 writes/word + #define EEESIZE 0x35 +#elif (EEPROM_SIZE == 256) // 315000 writes/byte or 630000 writes/word + #define EEESIZE 0x36 +#elif (EEPROM_SIZE == 128) // 635000 writes/byte or 1270000 writes/word + #define EEESIZE 0x37 +#elif (EEPROM_SIZE == 64) // 1275000 writes/byte or 2550000 writes/word + #define EEESIZE 0x38 +#elif (EEPROM_SIZE == 32) // 2555000 writes/byte or 5110000 writes/word + #define EEESIZE 0x39 +#endif + +void eeprom_initialize(void) +{ + uint32_t count=0; + uint16_t do_flash_cmd[] = { + 0xf06f, 0x037f, 0x7003, 0x7803, + 0xf013, 0x0f80, 0xd0fb, 0x4770}; + uint8_t status; + + if (FTFL->FCNFG & FTFL_FCNFG_RAMRDY) { + // FlexRAM is configured as traditional RAM + // We need to reconfigure for EEPROM usage + FTFL->FCCOB0 = 0x80; // PGMPART = Program Partition Command + FTFL->FCCOB4 = EEESIZE; // EEPROM Size + FTFL->FCCOB5 = 0x03; // 0K for Dataflash, 32K for EEPROM backup + __disable_irq(); + // do_flash_cmd() must execute from RAM. Luckily the C syntax is simple... + (*((void (*)(volatile uint8_t *))((uint32_t)do_flash_cmd | 1)))(&(FTFL->FSTAT)); + __enable_irq(); + status = FTFL->FSTAT; + if (status & (FTFL_FSTAT_RDCOLERR|FTFL_FSTAT_ACCERR|FTFL_FSTAT_FPVIOL)) { + FTFL->FSTAT = (status & (FTFL_FSTAT_RDCOLERR|FTFL_FSTAT_ACCERR|FTFL_FSTAT_FPVIOL)); + return; // error + } + } + // wait for eeprom to become ready (is this really necessary?) + while (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) { + if (++count > 20000) break; + } +} + +#define FlexRAM ((uint8_t *)0x14000000) + +uint8_t eeprom_read_byte(const uint8_t *addr) +{ + uint32_t offset = (uint32_t)addr; + if (offset >= EEPROM_SIZE) return 0; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); + return FlexRAM[offset]; +} + +uint16_t eeprom_read_word(const uint16_t *addr) +{ + uint32_t offset = (uint32_t)addr; + if (offset >= EEPROM_SIZE-1) return 0; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); + return *(uint16_t *)(&FlexRAM[offset]); +} + +uint32_t eeprom_read_dword(const uint32_t *addr) +{ + uint32_t offset = (uint32_t)addr; + if (offset >= EEPROM_SIZE-3) return 0; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); + return *(uint32_t *)(&FlexRAM[offset]); +} + +void eeprom_read_block(void *buf, const void *addr, uint32_t len) +{ + uint32_t offset = (uint32_t)addr; + uint8_t *dest = (uint8_t *)buf; + uint32_t end = offset + len; + + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); + if (end > EEPROM_SIZE) end = EEPROM_SIZE; + while (offset < end) { + *dest++ = FlexRAM[offset++]; + } +} + +int eeprom_is_ready(void) +{ + return (FTFL->FCNFG & FTFL_FCNFG_EEERDY) ? 1 : 0; +} + +static void flexram_wait(void) +{ + while (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) { + // TODO: timeout + } +} + +void eeprom_write_byte(uint8_t *addr, uint8_t value) +{ + uint32_t offset = (uint32_t)addr; + + if (offset >= EEPROM_SIZE) return; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); + if (FlexRAM[offset] != value) { + FlexRAM[offset] = value; + flexram_wait(); + } +} + +void eeprom_write_word(uint16_t *addr, uint16_t value) +{ + uint32_t offset = (uint32_t)addr; + + if (offset >= EEPROM_SIZE-1) return; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); +#ifdef HANDLE_UNALIGNED_WRITES + if ((offset & 1) == 0) { +#endif + if (*(uint16_t *)(&FlexRAM[offset]) != value) { + *(uint16_t *)(&FlexRAM[offset]) = value; + flexram_wait(); + } +#ifdef HANDLE_UNALIGNED_WRITES + } else { + if (FlexRAM[offset] != value) { + FlexRAM[offset] = value; + flexram_wait(); + } + if (FlexRAM[offset + 1] != (value >> 8)) { + FlexRAM[offset + 1] = value >> 8; + flexram_wait(); + } + } +#endif +} + +void eeprom_write_dword(uint32_t *addr, uint32_t value) +{ + uint32_t offset = (uint32_t)addr; + + if (offset >= EEPROM_SIZE-3) return; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); +#ifdef HANDLE_UNALIGNED_WRITES + switch (offset & 3) { + case 0: +#endif + if (*(uint32_t *)(&FlexRAM[offset]) != value) { + *(uint32_t *)(&FlexRAM[offset]) = value; + flexram_wait(); + } + return; +#ifdef HANDLE_UNALIGNED_WRITES + case 2: + if (*(uint16_t *)(&FlexRAM[offset]) != value) { + *(uint16_t *)(&FlexRAM[offset]) = value; + flexram_wait(); + } + if (*(uint16_t *)(&FlexRAM[offset + 2]) != (value >> 16)) { + *(uint16_t *)(&FlexRAM[offset + 2]) = value >> 16; + flexram_wait(); + } + return; + default: + if (FlexRAM[offset] != value) { + FlexRAM[offset] = value; + flexram_wait(); + } + if (*(uint16_t *)(&FlexRAM[offset + 1]) != (value >> 8)) { + *(uint16_t *)(&FlexRAM[offset + 1]) = value >> 8; + flexram_wait(); + } + if (FlexRAM[offset + 3] != (value >> 24)) { + FlexRAM[offset + 3] = value >> 24; + flexram_wait(); + } + } +#endif +} + +void eeprom_write_block(const void *buf, void *addr, uint32_t len) +{ + uint32_t offset = (uint32_t)addr; + const uint8_t *src = (const uint8_t *)buf; + + if (offset >= EEPROM_SIZE) return; + if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); + if (len >= EEPROM_SIZE) len = EEPROM_SIZE; + if (offset + len >= EEPROM_SIZE) len = EEPROM_SIZE - offset; + while (len > 0) { + uint32_t lsb = offset & 3; + if (lsb == 0 && len >= 4) { + // write aligned 32 bits + uint32_t val32; + val32 = *src++; + val32 |= (*src++ << 8); + val32 |= (*src++ << 16); + val32 |= (*src++ << 24); + if (*(uint32_t *)(&FlexRAM[offset]) != val32) { + *(uint32_t *)(&FlexRAM[offset]) = val32; + flexram_wait(); + } + offset += 4; + len -= 4; + } else if ((lsb == 0 || lsb == 2) && len >= 2) { + // write aligned 16 bits + uint16_t val16; + val16 = *src++; + val16 |= (*src++ << 8); + if (*(uint16_t *)(&FlexRAM[offset]) != val16) { + *(uint16_t *)(&FlexRAM[offset]) = val16; + flexram_wait(); + } + offset += 2; + len -= 2; + } else { + // write 8 bits + uint8_t val8 = *src++; + if (FlexRAM[offset] != val8) { + FlexRAM[offset] = val8; + flexram_wait(); + } + offset++; + len--; + } + } +} + +/* +void do_flash_cmd(volatile uint8_t *fstat) +{ + *fstat = 0x80; + while ((*fstat & 0x80) == 0) ; // wait +} +00000000 : + 0: f06f 037f mvn.w r3, #127 ; 0x7f + 4: 7003 strb r3, [r0, #0] + 6: 7803 ldrb r3, [r0, #0] + 8: f013 0f80 tst.w r3, #128 ; 0x80 + c: d0fb beq.n 6 + e: 4770 bx lr +*/ + +#elif defined(KL2x) /* chip selection */ +/* Teensy LC (emulated) */ + +#define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0)) + +extern uint32_t __eeprom_workarea_start__; +extern uint32_t __eeprom_workarea_end__; + +#define EEPROM_SIZE 128 + +static uint32_t flashend = 0; + +void eeprom_initialize(void) +{ + const uint16_t *p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); + + do { + if (*p++ == 0xFFFF) { + flashend = (uint32_t)(p - 2); + return; + } + } while (p < (uint16_t *)SYMVAL(__eeprom_workarea_end__)); + flashend = (uint32_t)((uint16_t *)SYMVAL(__eeprom_workarea_end__) - 1); +} + +uint8_t eeprom_read_byte(const uint8_t *addr) +{ + uint32_t offset = (uint32_t)addr; + const uint16_t *p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); + const uint16_t *end = (const uint16_t *)((uint32_t)flashend); + uint16_t val; + uint8_t data=0xFF; + + if (!end) { + eeprom_initialize(); + end = (const uint16_t *)((uint32_t)flashend); + } + if (offset < EEPROM_SIZE) { + while (p <= end) { + val = *p++; + if ((val & 255) == offset) data = val >> 8; + } + } + return data; +} + +static void flash_write(const uint16_t *code, uint32_t addr, uint32_t data) +{ + // with great power comes great responsibility.... + uint32_t stat; + *(uint32_t *)&(FTFA->FCCOB3) = 0x06000000 | (addr & 0x00FFFFFC); + *(uint32_t *)&(FTFA->FCCOB7) = data; + __disable_irq(); + (*((void (*)(volatile uint8_t *))((uint32_t)code | 1)))(&(FTFA->FSTAT)); + __enable_irq(); + stat = FTFA->FSTAT & (FTFA_FSTAT_RDCOLERR|FTFA_FSTAT_ACCERR|FTFA_FSTAT_FPVIOL); + if (stat) { + FTFA->FSTAT = stat; + } + MCM->PLACR |= MCM_PLACR_CFCC; +} + +void eeprom_write_byte(uint8_t *addr, uint8_t data) +{ + uint32_t offset = (uint32_t)addr; + const uint16_t *p, *end = (const uint16_t *)((uint32_t)flashend); + uint32_t i, val, flashaddr; + uint16_t do_flash_cmd[] = { + 0x2380, 0x7003, 0x7803, 0xb25b, 0x2b00, 0xdafb, 0x4770}; + uint8_t buf[EEPROM_SIZE]; + + if (offset >= EEPROM_SIZE) return; + if (!end) { + eeprom_initialize(); + end = (const uint16_t *)((uint32_t)flashend); + } + if (++end < (uint16_t *)SYMVAL(__eeprom_workarea_end__)) { + val = (data << 8) | offset; + flashaddr = (uint32_t)end; + flashend = flashaddr; + if ((flashaddr & 2) == 0) { + val |= 0xFFFF0000; + } else { + val <<= 16; + val |= 0x0000FFFF; + } + flash_write(do_flash_cmd, flashaddr, val); + } else { + for (i=0; i < EEPROM_SIZE; i++) { + buf[i] = 0xFF; + } + for (p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); p < (uint16_t *)SYMVAL(__eeprom_workarea_end__); p++) { + val = *p; + if ((val & 255) < EEPROM_SIZE) { + buf[val & 255] = val >> 8; + } + } + buf[offset] = data; + for (flashaddr=(uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_start__); flashaddr < (uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_end__); flashaddr += 1024) { + *(uint32_t *)&(FTFA->FCCOB3) = 0x09000000 | flashaddr; + __disable_irq(); + (*((void (*)(volatile uint8_t *))((uint32_t)do_flash_cmd | 1)))(&(FTFA->FSTAT)); + __enable_irq(); + val = FTFA->FSTAT & (FTFA_FSTAT_RDCOLERR|FTFA_FSTAT_ACCERR|FTFA_FSTAT_FPVIOL);; + if (val) FTFA->FSTAT = val; + MCM->PLACR |= MCM_PLACR_CFCC; + } + flashaddr=(uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_start__); + for (i=0; i < EEPROM_SIZE; i++) { + if (buf[i] == 0xFF) continue; + if ((flashaddr & 2) == 0) { + val = (buf[i] << 8) | i; + } else { + val = val | (buf[i] << 24) | (i << 16); + flash_write(do_flash_cmd, flashaddr, val); + } + flashaddr += 2; + } + flashend = flashaddr; + if ((flashaddr & 2)) { + val |= 0xFFFF0000; + flash_write(do_flash_cmd, flashaddr, val); + } + } +} + +/* +void do_flash_cmd(volatile uint8_t *fstat) +{ + *fstat = 0x80; + while ((*fstat & 0x80) == 0) ; // wait +} +00000000 : + 0: 2380 movs r3, #128 ; 0x80 + 2: 7003 strb r3, [r0, #0] + 4: 7803 ldrb r3, [r0, #0] + 6: b25b sxtb r3, r3 + 8: 2b00 cmp r3, #0 + a: dafb bge.n 4 + c: 4770 bx lr +*/ + + +uint16_t eeprom_read_word(const uint16_t *addr) +{ + const uint8_t *p = (const uint8_t *)addr; + return eeprom_read_byte(p) | (eeprom_read_byte(p+1) << 8); +} + +uint32_t eeprom_read_dword(const uint32_t *addr) +{ + const uint8_t *p = (const uint8_t *)addr; + return eeprom_read_byte(p) | (eeprom_read_byte(p+1) << 8) + | (eeprom_read_byte(p+2) << 16) | (eeprom_read_byte(p+3) << 24); +} + +void eeprom_read_block(void *buf, const void *addr, uint32_t len) +{ + const uint8_t *p = (const uint8_t *)addr; + uint8_t *dest = (uint8_t *)buf; + while (len--) { + *dest++ = eeprom_read_byte(p++); + } +} + +int eeprom_is_ready(void) +{ + return 1; +} + +void eeprom_write_word(uint16_t *addr, uint16_t value) +{ + uint8_t *p = (uint8_t *)addr; + eeprom_write_byte(p++, value); + eeprom_write_byte(p, value >> 8); +} + +void eeprom_write_dword(uint32_t *addr, uint32_t value) +{ + uint8_t *p = (uint8_t *)addr; + eeprom_write_byte(p++, value); + eeprom_write_byte(p++, value >> 8); + eeprom_write_byte(p++, value >> 16); + eeprom_write_byte(p, value >> 24); +} + +void eeprom_write_block(const void *buf, void *addr, uint32_t len) +{ + uint8_t *p = (uint8_t *)addr; + const uint8_t *src = (const uint8_t *)buf; + while (len--) { + eeprom_write_byte(p++, *src++); + } +} + +#else +#error EEPROM support not implemented for your chip +#endif /* chip selection */ + + +/*****************/ +/* TMK functions */ +/*****************/ + +void eeconfig_init(void) +{ + eeprom_write_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER); + eeprom_write_byte(EECONFIG_DEBUG, 0); + eeprom_write_byte(EECONFIG_DEFAULT_LAYER, 0); + eeprom_write_byte(EECONFIG_KEYMAP, 0); + eeprom_write_byte(EECONFIG_MOUSEKEY_ACCEL, 0); +#ifdef BACKLIGHT_ENABLE + eeprom_write_byte(EECONFIG_BACKLIGHT, 0); +#endif +} + +void eeconfig_enable(void) +{ + eeprom_write_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER); +} + +void eeconfig_disable(void) +{ + eeprom_write_word(EECONFIG_MAGIC, 0xFFFF); +} + +bool eeconfig_is_enabled(void) +{ + return (eeprom_read_word(EECONFIG_MAGIC) == EECONFIG_MAGIC_NUMBER); +} + +uint8_t eeconfig_read_debug(void) { return eeprom_read_byte(EECONFIG_DEBUG); } +void eeconfig_write_debug(uint8_t val) { eeprom_write_byte(EECONFIG_DEBUG, val); } + +uint8_t eeconfig_read_default_layer(void) { return eeprom_read_byte(EECONFIG_DEFAULT_LAYER); } +void eeconfig_write_default_layer(uint8_t val) { eeprom_write_byte(EECONFIG_DEFAULT_LAYER, val); } + +uint8_t eeconfig_read_keymap(void) { return eeprom_read_byte(EECONFIG_KEYMAP); } +void eeconfig_write_keymap(uint8_t val) { eeprom_write_byte(EECONFIG_KEYMAP, val); } + +#ifdef BACKLIGHT_ENABLE +uint8_t eeconfig_read_backlight(void) { return eeprom_read_byte(EECONFIG_BACKLIGHT); } +void eeconfig_write_backlight(uint8_t val) { eeprom_write_byte(EECONFIG_BACKLIGHT, val); } +#endif diff --git a/common/chibios/printf.c b/common/chibios/printf.c new file mode 100644 index 00000000..72e3d4f8 --- /dev/null +++ b/common/chibios/printf.c @@ -0,0 +1,240 @@ +/* + * found at: http://www.sparetimelabs.com/tinyprintf/tinyprintf.php + * and: http://www.sparetimelabs.com/printfrevisited/printfrevisited.php + */ + +/* +File: printf.c + +Copyright (C) 2004 Kustaa Nyholm + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "printf.h" + +typedef void (*putcf) (void*,char); +static putcf stdout_putf; +static void* stdout_putp; + +// this adds cca 400 bytes +#define PRINTF_LONG_SUPPORT + +#ifdef PRINTF_LONG_SUPPORT + +static void uli2a(unsigned long int num, unsigned int base, int uc,char * bf) + { + int n=0; + unsigned int d=1; + while (num/d >= base) + d*=base; + while (d!=0) { + int dgt = num / d; + num%=d; + d/=base; + if (n || dgt>0|| d==0) { + *bf++ = dgt+(dgt<10 ? '0' : (uc ? 'A' : 'a')-10); + ++n; + } + } + *bf=0; + } + +static void li2a (long num, char * bf) + { + if (num<0) { + num=-num; + *bf++ = '-'; + } + uli2a(num,10,0,bf); + } + +#endif + +static void ui2a(unsigned int num, unsigned int base, int uc,char * bf) + { + int n=0; + unsigned int d=1; + while (num/d >= base) + d*=base; + while (d!=0) { + int dgt = num / d; + num%= d; + d/=base; + if (n || dgt>0 || d==0) { + *bf++ = dgt+(dgt<10 ? '0' : (uc ? 'A' : 'a')-10); + ++n; + } + } + *bf=0; + } + +static void i2a (int num, char * bf) + { + if (num<0) { + num=-num; + *bf++ = '-'; + } + ui2a(num,10,0,bf); + } + +static int a2d(char ch) + { + if (ch>='0' && ch<='9') + return ch-'0'; + else if (ch>='a' && ch<='f') + return ch-'a'+10; + else if (ch>='A' && ch<='F') + return ch-'A'+10; + else return -1; + } + +static char a2i(char ch, char** src,int base,int* nump) + { + char* p= *src; + int num=0; + int digit; + while ((digit=a2d(ch))>=0) { + if (digit>base) break; + num=num*base+digit; + ch=*p++; + } + *src=p; + *nump=num; + return ch; + } + +static void putchw(void* putp,putcf putf,int n, char z, char* bf) + { + char fc=z? '0' : ' '; + char ch; + char* p=bf; + while (*p++ && n > 0) + n--; + while (n-- > 0) + putf(putp,fc); + while ((ch= *bf++)) + putf(putp,ch); + } + +void tfp_format(void* putp,putcf putf,char *fmt, va_list va) + { + char bf[12]; + + char ch; + + + while ((ch=*(fmt++))) { + if (ch!='%') + putf(putp,ch); + else { + char lz=0; +#ifdef PRINTF_LONG_SUPPORT + char lng=0; +#endif + int w=0; + ch=*(fmt++); + if (ch=='0') { + ch=*(fmt++); + lz=1; + } + if (ch>='0' && ch<='9') { + ch=a2i(ch,&fmt,10,&w); + } +#ifdef PRINTF_LONG_SUPPORT + if (ch=='l') { + ch=*(fmt++); + lng=1; + } +#endif + switch (ch) { + case 0: + goto abort; + case 'u' : { +#ifdef PRINTF_LONG_SUPPORT + if (lng) + uli2a(va_arg(va, unsigned long int),10,0,bf); + else +#endif + ui2a(va_arg(va, unsigned int),10,0,bf); + putchw(putp,putf,w,lz,bf); + break; + } + case 'd' : { +#ifdef PRINTF_LONG_SUPPORT + if (lng) + li2a(va_arg(va, unsigned long int),bf); + else +#endif + i2a(va_arg(va, int),bf); + putchw(putp,putf,w,lz,bf); + break; + } + case 'x': case 'X' : +#ifdef PRINTF_LONG_SUPPORT + if (lng) + uli2a(va_arg(va, unsigned long int),16,(ch=='X'),bf); + else +#endif + ui2a(va_arg(va, unsigned int),16,(ch=='X'),bf); + putchw(putp,putf,w,lz,bf); + break; + case 'c' : + putf(putp,(char)(va_arg(va, int))); + break; + case 's' : + putchw(putp,putf,w,0,va_arg(va, char*)); + break; + case '%' : + putf(putp,ch); + default: + break; + } + } + } + abort:; + } + + +void init_printf(void* putp,void (*putf) (void*,char)) + { + stdout_putf=putf; + stdout_putp=putp; + } + +void tfp_printf(char *fmt, ...) + { + va_list va; + va_start(va,fmt); + tfp_format(stdout_putp,stdout_putf,fmt,va); + va_end(va); + } + +static void putcp(void* p,char c) + { + *(*((char**)p))++ = c; + } + + + +void tfp_sprintf(char* s,char *fmt, ...) + { + va_list va; + va_start(va,fmt); + tfp_format(&s,putcp,fmt,va); + putcp(&s,0); + va_end(va); + } diff --git a/common/chibios/printf.h b/common/chibios/printf.h new file mode 100644 index 00000000..678a100c --- /dev/null +++ b/common/chibios/printf.h @@ -0,0 +1,111 @@ +/* + * found at: http://www.sparetimelabs.com/tinyprintf/tinyprintf.php + * and: http://www.sparetimelabs.com/printfrevisited/printfrevisited.php + */ + +/* +File: printf.h + +Copyright (C) 2004 Kustaa Nyholm + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +This library is realy just two files: 'printf.h' and 'printf.c'. + +They provide a simple and small (+200 loc) printf functionality to +be used in embedded systems. + +I've found them so usefull in debugging that I do not bother with a +debugger at all. + +They are distributed in source form, so to use them, just compile them +into your project. + +Two printf variants are provided: printf and sprintf. + +The formats supported by this implementation are: 'd' 'u' 'c' 's' 'x' 'X'. + +Zero padding and field width are also supported. + +If the library is compiled with 'PRINTF_SUPPORT_LONG' defined then the +long specifier is also +supported. Note that this will pull in some long math routines (pun intended!) +and thus make your executable noticably longer. + +The memory foot print of course depends on the target cpu, compiler and +compiler options, but a rough guestimate (based on a H8S target) is about +1.4 kB for code and some twenty 'int's and 'char's, say 60 bytes of stack space. +Not too bad. Your milage may vary. By hacking the source code you can +get rid of some hunred bytes, I'm sure, but personally I feel the balance of +functionality and flexibility versus code size is close to optimal for +many embedded systems. + +To use the printf you need to supply your own character output function, +something like : + + void putc ( void* p, char c) + { + while (!SERIAL_PORT_EMPTY) ; + SERIAL_PORT_TX_REGISTER = c; + } + +Before you can call printf you need to initialize it to use your +character output function with something like: + + init_printf(NULL,putc); + +Notice the 'NULL' in 'init_printf' and the parameter 'void* p' in 'putc', +the NULL (or any pointer) you pass into the 'init_printf' will eventually be +passed to your 'putc' routine. This allows you to pass some storage space (or +anything realy) to the character output function, if necessary. +This is not often needed but it was implemented like that because it made +implementing the sprintf function so neat (look at the source code). + +The code is re-entrant, except for the 'init_printf' function, so it +is safe to call it from interupts too, although this may result in mixed output. +If you rely on re-entrancy, take care that your 'putc' function is re-entrant! + +The printf and sprintf functions are actually macros that translate to +'tfp_printf' and 'tfp_sprintf'. This makes it possible +to use them along with 'stdio.h' printf's in a single source file. +You just need to undef the names before you include the 'stdio.h'. +Note that these are not function like macros, so if you have variables +or struct members with these names, things will explode in your face. +Without variadic macros this is the best we can do to wrap these +fucnction. If it is a problem just give up the macros and use the +functions directly or rename them. + +For further details see source code. + +regs Kusti, 23.10.2004 +*/ + + +#ifndef __TFP_PRINTF__ +#define __TFP_PRINTF__ + +#include + +void init_printf(void* putp,void (*putf) (void*,char)); + +void tfp_printf(char *fmt, ...); +void tfp_sprintf(char* s,char *fmt, ...); + +void tfp_format(void* putp,void (*putf) (void*,char),char *fmt, va_list va); + +#define printf tfp_printf +#define sprintf tfp_sprintf + +#endif diff --git a/common/chibios/sleep_led.c b/common/chibios/sleep_led.c new file mode 100644 index 00000000..50cf4eb7 --- /dev/null +++ b/common/chibios/sleep_led.c @@ -0,0 +1,164 @@ +#include "ch.h" +#include "hal.h" + +#include "led.h" +#include "sleep_led.h" + +#if defined(KL2x) || defined(K20x) /* platform selection: familiar Kinetis chips */ +/* All right, we go the "software" way: LP timer, toggle LED in interrupt. + * Based on hasu's code for AVRs. + */ + +/* Breathing Sleep LED brighness(PWM On period) table + * (64[steps] * 4[duration]) / 64[PWM periods/s] = 4 second breath cycle + * + * http://www.wolframalpha.com/input/?i=%28sin%28+x%2F64*pi%29**8+*+255%2C+x%3D0+to+63 + * (0..63).each {|x| p ((sin(x/64.0*PI)**8)*255).to_i } + */ +static const uint8_t breathing_table[64] = { +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 6, 10, +15, 23, 32, 44, 58, 74, 93, 113, 135, 157, 179, 199, 218, 233, 245, 252, +255, 252, 245, 233, 218, 199, 179, 157, 135, 113, 93, 74, 58, 44, 32, 23, +15, 10, 6, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* Low Power Timer interrupt handler */ +OSAL_IRQ_HANDLER(KINETIS_LPTMR0_IRQ_VECTOR) { + OSAL_IRQ_PROLOGUE(); + + /* Software PWM + * timer:1111 1111 1111 1111 + * \_____/\/ \_______/____ count(0-255) + * \ \______________ duration of step(4) + * \__________________ index of step table(0-63) + */ + + // this works for cca 65536 irqs/sec + static union { + uint16_t row; + struct { + uint8_t count:8; + uint8_t duration:2; + uint8_t index:6; + } pwm; + } timer = { .row = 0 }; + + timer.row++; + + // LED on + if (timer.pwm.count == 0) { + led_set(1<CSR |= LPTMRx_CSR_TCF; + + OSAL_IRQ_EPILOGUE(); +} + +/* LPTMR clock options */ +#define LPTMR_CLOCK_MCGIRCLK 0 /* 4MHz clock */ +#define LPTMR_CLOCK_LPO 1 /* 1kHz clock */ +#define LPTMR_CLOCK_ERCLK32K 2 /* external 32kHz crystal */ +#define LPTMR_CLOCK_OSCERCLK 3 /* output from OSC */ + +/* Work around inconsistencies in Freescale naming */ +#if !defined(SIM_SCGC5_LPTMR) +#define SIM_SCGC5_LPTMR SIM_SCGC5_LPTIMER +#endif + +/* Initialise the timer */ +void sleep_led_init(void) { + /* Make sure the clock to the LPTMR is enabled */ + SIM->SCGC5 |= SIM_SCGC5_LPTMR; + /* Reset LPTMR settings */ + LPTMR0->CSR = 0; + /* Set the compare value */ + LPTMR0->CMR = 0; // trigger on counter value (i.e. every time) + + /* Set up clock source and prescaler */ + /* Software PWM + * ______ ______ __ + * | ON |___OFF___| ON |___OFF___| .... + * |<-------------->|<-------------->|<- .... + * PWM period PWM period + * + * R interrupts/period[resolution] + * F periods/second[frequency] + * R * F interrupts/second + */ + + /* === OPTION 1 === */ + #if 0 + // 1kHz LPO + // No prescaler => 1024 irqs/sec + // Note: this is too slow for a smooth breathe + LPTMR0->PSR = LPTMRx_PSR_PCS(LPTMR_CLOCK_LPO)|LPTMRx_PSR_PBYP; + #endif /* OPTION 1 */ + + /* === OPTION 2 === */ + #if 1 + // nMHz IRC (n=4 on KL25Z, KL26Z and K20x; n=2 or 8 on KL27Z) + MCG->C2 |= MCG_C2_IRCS; // fast (4MHz) internal ref clock + #if defined(KL27) // divide the 8MHz IRC by 2, to have the same MCGIRCLK speed as others + MCG->MC |= MCG_MC_LIRC_DIV2_DIV2; + #endif /* KL27 */ + MCG->C1 |= MCG_C1_IRCLKEN; // enable internal ref clock + // to work in stop mode, also MCG_C1_IREFSTEN + // Divide 4MHz by 2^N (N=6) => 62500 irqs/sec => + // => approx F=61, R=256, duration = 4 + LPTMR0->PSR = LPTMRx_PSR_PCS(LPTMR_CLOCK_MCGIRCLK)|LPTMRx_PSR_PRESCALE(6); + #endif /* OPTION 2 */ + + /* === OPTION 3 === */ + #if 0 + // OSC output (external crystal), usually 8MHz or 16MHz + OSC0->CR |= OSC_CR_ERCLKEN; // enable ext ref clock + // to work in stop mode, also OSC_CR_EREFSTEN + // Divide by 2^N + LPTMR0->PSR = LPTMRx_PSR_PCS(LPTMR_CLOCK_OSCERCLK)|LPTMRx_PSR_PRESCALE(7); + #endif /* OPTION 3 */ + /* === END OPTIONS === */ + + /* Interrupt on TCF set (compare flag) */ + nvicEnableVector(LPTMR0_IRQn, 2); // vector, priority + LPTMR0->CSR |= LPTMRx_CSR_TIE; +} + +void sleep_led_enable(void) { + /* Enable the timer */ + LPTMR0->CSR |= LPTMRx_CSR_TEN; +} + +void sleep_led_disable(void) { + /* Disable the timer */ + LPTMR0->CSR &= ~LPTMRx_CSR_TEN; +} + +void sleep_led_toggle(void) { + /* Toggle the timer */ + LPTMR0->CSR ^= LPTMRx_CSR_TEN; +} + +#else /* platform selection: not on familiar Kinetis chips */ + +void sleep_led_init(void) { +} + +void sleep_led_enable(void) { + led_set(1<. */ #include #include -#include +#include "wait.h" #include "keycode.h" #include "host.h" #include "keymap.h" @@ -97,12 +97,14 @@ bool command_proc(uint8_t code) bool command_extra(uint8_t code) __attribute__ ((weak)); bool command_extra(uint8_t code) { + (void)code; return false; } bool command_console_extra(uint8_t code) __attribute__ ((weak)); bool command_console_extra(uint8_t code) { + (void)code; return false; } @@ -178,7 +180,9 @@ static void print_eeconfig(void) static bool command_common(uint8_t code) { +#ifdef KEYBOARD_LOCK_ENABLE static host_driver_t *host_driver = 0; +#endif switch (code) { #ifdef SLEEP_LED_ENABLE case KC_Z: @@ -223,7 +227,7 @@ static bool command_common(uint8_t code) case KC_PAUSE: clear_keyboard(); print("\n\nbootloader... "); - _delay_ms(1000); + wait_ms(1000); bootloader_jump(); // not return break; case KC_D: @@ -283,6 +287,9 @@ static bool command_common(uint8_t code) #ifdef PROTOCOL_VUSB " VUSB" #endif +#ifdef PROTOCOL_CHIBIOS + " CHIBIOS" +#endif #ifdef BOOTMAGIC_ENABLE " BOOTMAGIC" #endif @@ -307,8 +314,13 @@ static bool command_common(uint8_t code) " " STR(BOOTLOADER_SIZE) "\n"); print("GCC: " STR(__GNUC__) "." STR(__GNUC_MINOR__) "." STR(__GNUC_PATCHLEVEL__) +#if defined(__AVR__) " AVR-LIBC: " __AVR_LIBC_VERSION_STRING__ " AVR_ARCH: avr" STR(__AVR_ARCH__) "\n"); +#elif defined(__arm__) + // TODO + ); +#endif break; case KC_S: print("\n\t- Status -\n"); @@ -318,7 +330,7 @@ static bool command_common(uint8_t code) #ifdef NKRO_ENABLE print_val_hex8(keyboard_nkro); #endif - print_val_hex32(timer_count); + print_val_hex32(timer_read32()); #ifdef PROTOCOL_PJRC print_val_hex8(UDCON); @@ -338,10 +350,11 @@ static bool command_common(uint8_t code) case KC_N: clear_keyboard(); //Prevents stuck keys. keyboard_nkro = !keyboard_nkro; - if (keyboard_nkro) + if (keyboard_nkro) { print("NKRO: on\n"); - else + } else { print("NKRO: off\n"); + } break; #endif case KC_ESC: @@ -595,10 +608,11 @@ static bool mousekey_console(uint8_t code) print("?"); return false; } - if (mousekey_param) + if (mousekey_param) { xprintf("M%d> ", mousekey_param); - else + } else { print("M>" ); + } return true; } #endif diff --git a/common/keycode.h b/common/keycode.h index 44819530..f3402712 100644 --- a/common/keycode.h +++ b/common/keycode.h @@ -85,15 +85,19 @@ along with this program. If not, see . #define KC_LCAP KC_LOCKING_CAPS #define KC_LNUM KC_LOCKING_NUM #define KC_LSCR KC_LOCKING_SCROLL -#define KC_ERAS KC_ALT_ERASE, +#define KC_ERAS KC_ALT_ERASE #define KC_CLR KC_CLEAR /* Japanese specific */ #define KC_ZKHK KC_GRAVE #define KC_RO KC_INT1 #define KC_KANA KC_INT2 #define KC_JYEN KC_INT3 +#define KC_JPY KC_INT3 #define KC_HENK KC_INT4 #define KC_MHEN KC_INT5 +/* Korean specific */ +#define KC_HAEN KC_LANG1 +#define KC_HANJ KC_LANG2 /* Keypad */ #define KC_P1 KC_KP_1 #define KC_P2 KC_KP_2 @@ -113,6 +117,11 @@ along with this program. If not, see . #define KC_PPLS KC_KP_PLUS #define KC_PEQL KC_KP_EQUAL #define KC_PENT KC_KP_ENTER +/* Unix function key */ +#define KC_EXEC KC_EXECUTE +#define KC_SLCT KC_SELECT +#define KC_AGIN KC_AGAIN +#define KC_PSTE KC_PASTE /* Mousekey */ #define KC_MS_U KC_MS_UP #define KC_MS_D KC_MS_DOWN diff --git a/common/keymap.c b/common/keymap.c index d4892380..134e01c2 100644 --- a/common/keymap.c +++ b/common/keymap.c @@ -22,7 +22,11 @@ along with this program. If not, see . #include "action_macro.h" #include "wait.h" #include "debug.h" +#include "bootloader.h" +#ifdef BOOTMAGIC_ENABLE +extern keymap_config_t keymap_config; +#endif static action_t keycode_to_action(uint8_t keycode); @@ -109,6 +113,9 @@ action_t action_for_key(uint8_t layer, keypos_t key) __attribute__ ((weak)) const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) { + (void)record; + (void)id; + (void)opt; return MACRO_NONE; } @@ -116,6 +123,9 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) __attribute__ ((weak)) void action_function(keyrecord_t *record, uint8_t id, uint8_t opt) { + (void)record; + (void)id; + (void)opt; } @@ -123,7 +133,7 @@ void action_function(keyrecord_t *record, uint8_t id, uint8_t opt) /* translates keycode to action */ static action_t keycode_to_action(uint8_t keycode) { - action_t action; + action_t action = {}; switch (keycode) { case KC_A ... KC_EXSEL: case KC_LCTRL ... KC_RGUI: diff --git a/common/keymap.h b/common/keymap.h index e1a6f992..659ea357 100644 --- a/common/keymap.h +++ b/common/keymap.h @@ -38,7 +38,6 @@ typedef union { bool nkro:1; }; } keymap_config_t; -keymap_config_t keymap_config; #endif diff --git a/common/nodebug.h b/common/nodebug.h index 93309ada..6ef9f91a 100644 --- a/common/nodebug.h +++ b/common/nodebug.h @@ -18,7 +18,9 @@ along with this program. If not, see . #ifndef NODEBUG_H #define NODEBUG_H 1 +#if !defined(NO_DEBUG) #define NO_DEBUG +#endif #include "debug.h" #undef NO_DEBUG diff --git a/common/print.c b/common/print.c index ca94e1e5..00489557 100644 --- a/common/print.c +++ b/common/print.c @@ -38,11 +38,15 @@ void print_set_sendchar(int8_t (*sendchar_func)(uint8_t)) xdev_out(sendchar_func); } -#elif defined(__arm__) +#elif defined(PROTOCOL_CHIBIOS) /* __AVR__ */ + +// don't need anything extra + +#elif defined(__arm__) /* __AVR__ */ // TODO //void print_set_sendchar(int8_t (*sendchar_func)(uint8_t)) { } -#endif +#endif /* __AVR__ */ #endif diff --git a/common/print.h b/common/print.h index c0e9e143..5f491aba 100644 --- a/common/print.h +++ b/common/print.h @@ -47,7 +47,15 @@ extern "C" /* function pointer of sendchar to be used by print utility */ void print_set_sendchar(int8_t (*print_sendchar_func)(uint8_t)); -#elif defined(__arm__) +#elif defined(PROTOCOL_CHIBIOS) /* __AVR__ */ + +#include "chibios/printf.h" + +#define print(s) printf(s) +#define println(s) printf(s "\r\n") +#define xprintf printf + +#elif defined(__arm__) /* __AVR__ */ #include "mbed/xprintf.h" @@ -91,9 +99,9 @@ void print_set_sendchar(int8_t (*print_sendchar_func)(uint8_t)); #else /* NO_PRINT */ -#define xprintf -#define print -#define println +#define xprintf(s,...) +#define print(s) +#define println(s) #define print_set_sendchar(func) #define print_dec(data) #define print_decs(data) @@ -119,6 +127,7 @@ void print_set_sendchar(int8_t (*print_sendchar_func)(uint8_t)); #define print_val_bin_reverse8(v) #define print_val_bin_reverse16(v) #define print_val_bin_reverse32(v) +#define init_printf(s,ss) #endif /* NO_PRINT */ diff --git a/common/progmem.h b/common/progmem.h index 199b1bed..5b276562 100644 --- a/common/progmem.h +++ b/common/progmem.h @@ -5,8 +5,8 @@ # include #elif defined(__arm__) # define PROGMEM -# define pgm_read_byte(p) *(p) -# define pgm_read_word(p) *(p) +# define pgm_read_byte(p) *((unsigned char*)p) +# define pgm_read_word(p) *((uint16_t*)p) #endif #endif diff --git a/common/report.h b/common/report.h index f6c0a315..0c799eca 100644 --- a/common/report.h +++ b/common/report.h @@ -84,6 +84,11 @@ along with this program. If not, see . # define KEYBOARD_REPORT_SIZE NKRO_EPSIZE # define KEYBOARD_REPORT_KEYS (NKRO_EPSIZE - 2) # define KEYBOARD_REPORT_BITS (NKRO_EPSIZE - 1) +#elif defined(PROTOCOL_CHIBIOS) && defined(NKRO_ENABLE) +# include "protocol/chibios/usb_main.h" +# define KEYBOARD_REPORT_SIZE NKRO_EPSIZE +# define KEYBOARD_REPORT_KEYS (NKRO_EPSIZE - 2) +# define KEYBOARD_REPORT_BITS (NKRO_EPSIZE - 1) #else # define KEYBOARD_REPORT_SIZE 8 diff --git a/common/wait.h b/common/wait.h index 40d00b0c..82727be0 100644 --- a/common/wait.h +++ b/common/wait.h @@ -9,9 +9,13 @@ extern "C" { # include # define wait_ms(ms) _delay_ms(ms) # define wait_us(us) _delay_us(us) -#elif defined(__arm__) +#elif defined(PROTOCOL_CHIBIOS) /* __AVR__ */ +# include "ch.h" +# define wait_ms(ms) chThdSleepMilliseconds(ms) +# define wait_us(us) chThdSleepMicroseconds(us) +#elif defined(__arm__) /* __AVR__ */ # include "wait_api.h" -#endif +#endif /* __AVR__ */ #ifdef __cplusplus } diff --git a/doc/build.md b/doc/build.md index 20702e94..57a36ac4 100644 --- a/doc/build.md +++ b/doc/build.md @@ -6,11 +6,11 @@ Download and Install -------------------- ### 1. Install Tools -1. **Toolchain** On Windows install [MHV AVR Tools][mhv] for AVR GCC compiler and [Cygwin][cygwin](or [MinGW][mingw]) for shell terminal. On Mac you can use [CrossPack][crosspack]. On Linux you can install AVR GCC with your favorite package manager. +1. **Toolchain** On Windows install [Atmel AVR Toolchain][atmelgcc] for AVR GCC compiler and [Cygwin][cygwin](or [MinGW][mingw]) for shell terminal. On Mac you can use [CrossPack][crosspack] or your favorite package manager. On Linux you can install AVR GCC with your favorite package manager. -2. **Programmer** On Windows install [Atmel FLIP][flip]. On Mac and Linux install [dfu-programmer][dfu-prog]. +2. **Programmer** Install [dfu-programmer][dfu-prog]. GUI tool [Atmel FLIP][flip] also can be used on Windows. -3. **Driver** On Windows you start DFU bootloader on the chip first time you will see 'Found New Hardware Wizard' to install driver. If you install device driver properly you can find chip name like 'ATmega32U4' under 'LibUSB-Win32 Devices' tree on 'Device Manager'. If not you shall need to update its driver on 'Device Manager'. You will find the driver in `FLIP` install directory like: C:\Program Files (x86)\Atmel\Flip 3.4.5\usb\. In case of `dfu-programmer` use its driver. +3. **Driver** On Windows when you start DFU bootloader on the chip first time you will see **Found New Hardware Wizard** to install driver. If you install device driver properly you will find chip name like **ATmega32U4** under **LibUSB-Win32 Devices** tree on **Device Manager**. If not you shall need to update its driver on **Device Manager**. You will find the driver in `FLIP` install directory like: `C:\Program Files (x86)\Atmel\Flip 3.4.5\usb\`. In case of `dfu-programmer` install driver distributed with it. If you use PJRC Teensy you don't need step 2 and 3 above, just get [Teensy loader][teensy-loader]. @@ -20,7 +20,7 @@ You can find firmware source at github: - -If you are familiar with `Git` tools you are recommended to use it but you can also download zip archive from: +If you are familiar with [`git`][git] tools you are recommended to use it but you can also download zip archive from: - @@ -28,7 +28,7 @@ If you are familiar with `Git` tools you are recommended to use it but you can a Build firmware -------------- ### 1. Open terminal -Open terminal window to get access to commands. Use Cygwin(or MingGW) `shell terminal` in Windows or `Terminal.app` on Mac OSX. In Windows press `Windows` key and `R` then enter `cmd` in 'Run command' dialog showing up. +Open terminal window to get access to commands. Use Cygwin(or MingGW) shell terminal in Windows or `Terminal.app` on Mac OSX. ### 2. Change directory Move to project directory in the firmware source. @@ -53,60 +53,65 @@ Now you have **hex** file to program on current directory. This **hex** is only How to program controller depends on controller chip and its board design. To program AVR USB chips you'll need to start it up in bootloader mode. Most of boards with the chip have a push button to let bootloader come up. Consult with your controller board manual. ### 2. Program with DFU bootloader -Stock AVR USB chip including ATmega32U4 has DFU bootloader by factory default. `FLIP` is a DFU programmer on Windows offered by Atmel. Open source command line tool `dfu-programmer` also supports AVR chips, it runs on Linux, Mac OSX and even Windows. +Stock AVR USB chips have DFU bootloader by factory default. `FLIP` is a DFU programmer on Windows offered by Atmel. `FLIP` has two version of tool, GUI app and command line program. If you want GUI see tutorial below. Open source alternative `dfu-programmer` also supports AVR chips, it is command line tool and runs on Linux, Mac OSX and even Windows. -To program AVR chip with DFU bootloader use `FLIP` or `dfu-programmer`. -If you have a proper program command in `Makefile` just type this. -`FLIP` has two version of tool, GUI app and command line program. If you want GUI see tutorial below. -To use command line tool run this command. Note that you need to set PATH variable properly. +To program with command of `FLIP` run this. Note that you need to set PATH variable properly. $ make -f Makefile. flip -Or to program with `dfu-programmer` run: +With `dfu-programmer` run this. $ make -f Makefile. dfu -#### FLIP GUI tutorial -1. On menu bar click Device -> Select, then. `ATmega32u4`. -2. On menu bar click Settings -> Communication -> USB, then click 'Open' button on 'USB Port Connection' dialog. -At this point you'll see grey-outed widgets on the app get colored and ready. +Or you can execute the command directly as the following. -3. On menu bar click File -> Load HEX File, then select your firmware hex file on File Selector dialog. -4. On 'Operations Flow' panel click 'Run' button to load the firmware binary to the chip. Note that you should keep 'Erase', 'Blank Check', 'Program' and 'Verify' check boxes selected. -5. Re-plug USB cord or click 'Start Application' button to restart your controller. + $ dfu-programmer erase --force + $ dfu-programmer flash + $ dfu-programmer reset + +`` part will be `atmega32u4` or `atmega32u2` in most cases. See manual of the command for the detail. On Linux and Mac OSX you will need proper permission to program a controller and you can use `sudo` command for this purpose probably. On Linux you also can configure `udev` rules to set permission. + + +#### FLIP GUI tutorial +1. On menu bar click **Device** -> **Select**, then choose your chip name. (In most cases **ATmega32U2** or **ATmega32U4**) +2. On menu bar click **Settings** -> **Communication** -> **USB**, then click **Open** button on **USB Port Connection** dialog. At this point you'll have to plug into USB and start bootloader. + +3. On menu bar click **File** -> **Load HEX File**, then select your firmware hex file on File Selector dialog. +4. On **Operations Flow** panel click **Run** button to load the firmware binary to the chip. Note that you should keep **Erase**, **Blank Check**, **Program** and **Verify** check boxes selected. +5. Re-plug USB cord or click **Start Application** button to restart your controller. Done. -See also these instructions if you need. +See also these instructions if needed. - - -### 3. Program with Teensy Loader -If you have PJRC Teensy see instruction of `Teensy Loader`. +##### Troubleshoot +* **FLIP: AtLibUsbDfu.dll not found** +Remove current driver and re-install one FLIP provides from DeviceManager of WIndows. See -- -Or use this command if you have command line version of Teensy Loader installed. +### 3. Program with Other programmer +If you are using PJRC Teensy consult with instruction of [Teensy Loader][teensy-loader]. Or run this target with `make` after you install command line version of it. $ make -f Makefile. teensy - -### 4. Program with Other programmer -You may want to use other programmer like `avrdude` with AVRISPmkII, Arduino or USBasp. In that case you can still use make target `program` for build with configuring `PROGRAM_CMD` in Makefile. +You may want to use other programmer like [`avrdude`][avrdude]. In that case you can still use make target `program` for build with configuring `PROGRAM_CMD` in Makefile. See below. $ make -f Makefile. program +[atmelgcc]: http://www.atmel.com/tools/ATMELAVRTOOLCHAINFORWINDOWS.aspx [cygwin]: https://www.cygwin.com/ [mingw]: http://www.mingw.org/ -[mhv]: https://infernoembedded.com/products/avr-tools -[winavr]: http://winavr.sourceforge.net/ [crosspack]: http://www.obdev.at/products/crosspack/index.html [flip]: http://www.atmel.com/tools/FLIP.aspx [dfu-prog]: http://dfu-programmer.sourceforge.net/ [teensy-loader]:http://www.pjrc.com/teensy/loader.html +[avrdude]: http://savannah.nongnu.org/projects/avrdude/ +[git]: https://git-scm.com/ @@ -122,6 +127,7 @@ Set your MCU and its clock in Hz. # Boot Section Size in *bytes* # Teensy halfKay 512 + # Teensy++ halfKay 2048 # Atmel DFU loader 4096 # LUFA bootloader 4096 OPT_DEFS += -DBOOTLOADER_SIZE=4096 @@ -141,7 +147,7 @@ Optional. Note that ***comment out*** with `#` to disable them. #BACKLIGHT_ENABLE = yes # Enable keyboard backlight functionality ### 3. Programmer -Optional. Set proper command for your controller, bootloader and programmer. This command can be used with `make program`. Not needed if you use `FLIP`, `dfu-programmer` or `Teensy Loader`. +Optional. Set proper command for your controller, bootloader and programmer. This command can be used with `make program`. # avrdude with AVRISPmkII PROGRAM_CMD = avrdude -p $(MCU) -c avrispmkII -P USB -U flash:w:$(TARGET).hex diff --git a/doc/keymap.md b/doc/keymap.md index d4a129b2..566936c6 100644 --- a/doc/keymap.md +++ b/doc/keymap.md @@ -368,17 +368,37 @@ Default Layer also has bitwise operations, they are executed when key is release MACRO( I(255), T(H), T(E), T(L), T(L), W(255), T(O), END ) #### 2.3.1 Macro Commands +- **MACRO()** +- **MACRO_NONE** + - **I()** change interval of stroke. - **D()** press key - **U()** release key - **T()** type key(press and release) - **W()** wait +- **SM()** store modifier state +- **RM()** restore modifier state +- **CM()** clear modifier state - **END** end mark #### 2.3.2 Examples +***TBD*** + + const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) + { + switch (id) { + case HELLO: + return (record->event.pressed ? + MACRO( I(0), T(H), T(E), T(L), T(L), W(255), T(O), END ) : + MACRO_NONE ); + case ALT_TAB: + return (record->event.pressed ? + MACRO( D(LALT), D(TAB), END ) : + MACRO( U(TAB), END )); + } + return MACRO_NONE; + } -***TODO: sample implementation*** -See `keyboard/hhkb/keymap.c` for sample. diff --git a/doc/other_projects.md b/doc/other_projects.md deleted file mode 100644 index bf980b0a..00000000 --- a/doc/other_projects.md +++ /dev/null @@ -1,62 +0,0 @@ -Other Keyboard Firmware Projects -================================ -## PJRC USB Keyboard/Mouse Example[USB][PJRC][Teensy][AVR] -- -- - -## kbupgrade[USB][V-USB][AVR] -- -- - -## c64key[USB][V-USB][AVR] -- - -## rump[USB][V-USB][AVR] -- -- - -## dulcimer[USB][V-USB][AVR] -- - -## humblehacker-keyboard[USB][LUFA][AVR][Ergo] -- -- -- - -## ps2avr[PS/2][AVR] -- - -## ErgoDox[Ergo][Split][USB][AVR] -- -- -- - -## Suka's keyboard collection[Ergo][Split][3DPrinting][USB][AVR] -- -- - -## bpiphany's AVR-Keyboard[PJRC][AVR][USB] -- -- -- - -## USB-USB keyboard remapper[converter][USB-USB][AVR][Arduino] -- -- - -## USB-USB converter threads[converter][USB-USB] -- -- - -## kbdbabel.org[converter][vintage][protocol][8051] -Great resource of vintage keyboard protocol information and code - -- - -## Haata's kiibohd Controller[converter][vintage][protocol][AVR][PJRC][Cortex] -A lots of vintage keyboard protocol supports - -- - -## Kinesis ergonomic keyboard firmware replacement[V-USB][LUFA][Ergo] -- diff --git a/protocol/chibios/README.md b/protocol/chibios/README.md new file mode 100644 index 00000000..9796f8c7 --- /dev/null +++ b/protocol/chibios/README.md @@ -0,0 +1,50 @@ +## TMK running on top of ChibiOS + +### Notes + +- To use, unpack or symlink [ChibiOS] {currently 3.0.2} to `tmk_core/tool/chibios/chibios`. For Kinetis support, you'll need a fork which implements the USB driver, e.g. [this one](https://github.com/flabbergast/ChibiOS/tree/kinetis). +- For gcc options, inspect `tmk_core/tool/chibios/chibios.mk`. For instance, I enabled `-Wno-missing-field-initializers`, because TMK common bits generated a lot of warnings on that. +Also pay attention to `-O0` (enabled for debugging); for deployment use `-O2`. +- USB string descriptors are messy. I did not find a way to cleanly generate the right structures from actual strings, so the definitions in individual keyboards' `config.h` are ugly as heck. +- There are some random constants left so far, e.g. 5ms sleep between calling `keyboard_task`, or 1.5sec wait for USB init, in `main.c`. There should be no such in `usb_main.c` (the main USB stack). Everything is based on timers/interrupts/kernel scheduling (well except `keyboard_task`), so no periodically called things (again, except `keyboard_task`, which is just how TMK is designed). +- It is easy to add some code for testing (e.g. blink LED, do stuff on button press, etc...) - just create another thread in `main.c`, it will run independently of the keyboard business. +- Jumping to (the built-in) bootloaders on STM32 works, but it is not entirely pleasant, since it is very much MCU dependent. So, one needs to dig out the right address to jump to, and either pass it to the compiler in the `Makefile`, or better, define it in `/bootloader_defs.h`. Also, a patch to upstream ChibiOS is needed (supplied), because it `ResetHandler` needs adjusting. +- Sleep LED works, but at the moment only on/off, i.e. no breathing. + +### Immediate todo + +- host-wakeup packet sending during suspend +- power saving for suspend? +- PWM for sleep led + +### Not tested, but possibly working + +- backlight + +### Missing / not working (TMK vs ChibiOS bits) + +- eeprom / bootmagic for STM32 (will be chip dependent; eeprom needs to be emulated in flash, which means less writes; wear-levelling?) There is a semi-official ST "driver" for eeprom, with wear-levelling, but I think it consumes a lot of RAM (like 2 pages, i.e. 1kB or so). + +### Tried with + +- ChibiOS 3.0.1, 3.0.2 and ST F072RB DISCOVERY board. +- Need to test on other STM32 chips (F3, F4) to make it as much chip-independent as possible. +- ChibiOS with Kinetis patches and Teensy LC and 3.0. + +## ChibiOS-supported MCUs (as of 3.0.2) + +- Pretty much all STM32 chips. +- There is some support for K20x and KL2x Freescale chips (i.e. Teensy 3.x/LC, mchck, FRDM-KL2{5,6}Z, FRDM-K20D50M), but again, no official USB stack yet. However the `kinetis` branch of [my ChibiOS fork](https://github.com/flabbergast/ChibiOS). With this fork, TMK work normally on all the ARM Teensies. +- There is also support for AVR8, but the USB stack is not implemented for them yet, and also the kernel itself takes about 1k of RAM. I think people managed to get ChibiOS running on atmega32[8p/u4] though. +- I've seen community support for Nordic NRF51822 (the chip in Adafruit's Bluefruit bluetooth-low-energy boards), but not sure about the extent. + +## STM32-based keyboard design considerations + +- STM32F0x2 chips can do crystal-less USB, but they still need a 3.3V voltage regulator. +- The BOOT0 pin should be tied to GND. +- For a hardware way of accessing the in-built DFU bootloader, in addition to the reset button, put another button between the BOOT0 pin and 3V3. +- For breathing the caps lock LED during the suspended state ("sleep LED"), it is desirable to have that LED on a hardware PWM pin (there's usually plenty of those, look for TIMERs in the datasheet). However this is not strictly necessary, because instead of direct output of a timer to a pin (better of course), it is easy to define timer callbacks in ChibiOS that turn on/off an arbitrary pin. + + + +[ChibiOS]: http://chibios.org diff --git a/protocol/chibios/main.c b/protocol/chibios/main.c new file mode 100644 index 00000000..b62cfa9c --- /dev/null +++ b/protocol/chibios/main.c @@ -0,0 +1,139 @@ +/* + * (c) 2015 flabberast + * + * Based on the following work: + * - Guillaume Duc's raw hid example (MIT License) + * https://github.com/guiduc/usb-hid-chibios-example + * - PJRC Teensy examples (MIT License) + * https://www.pjrc.com/teensy/usb_keyboard.html + * - hasu's TMK keyboard code (GPL v2 and some code Modified BSD) + * https://github.com/tmk/tmk_keyboard/ + * - ChibiOS demo code (Apache 2.0 License) + * http://www.chibios.org + * + * Since some GPL'd code is used, this work is licensed under + * GPL v2 or later. + */ + +#include "ch.h" +#include "hal.h" + +#include "usb_main.h" + +/* TMK includes */ +#include "report.h" +#include "host.h" +#include "host_driver.h" +#include "keyboard.h" +#include "action.h" +#include "action_util.h" +#include "mousekey.h" +#include "led.h" +#include "sendchar.h" +#include "debug.h" +#ifdef SLEEP_LED_ENABLE +#include "sleep_led.h" +#endif +#include "suspend.h" + + +/* ------------------------- + * TMK host driver defs + * ------------------------- + */ + +/* declarations */ +uint8_t keyboard_leds(void); +void send_keyboard(report_keyboard_t *report); +void send_mouse(report_mouse_t *report); +void send_system(uint16_t data); +void send_consumer(uint16_t data); + +/* host struct */ +host_driver_t chibios_driver = { + keyboard_leds, + send_keyboard, + send_mouse, + send_system, + send_consumer +}; + + +/* TESTING + * Amber LED blinker thread, times are in milliseconds. + */ +/* set this variable to non-zero anywhere to blink once */ +// uint8_t blinkLed = 0; +// static THD_WORKING_AREA(waBlinkerThread, 128); +// static THD_FUNCTION(blinkerThread, arg) { +// (void)arg; +// chRegSetThreadName("blinkOrange"); +// while(true) { +// if(blinkLed) { +// blinkLed = 0; +// palSetPad(TEENSY_PIN13_IOPORT, TEENSY_PIN13); +// chThdSleepMilliseconds(100); +// palClearPad(TEENSY_PIN13_IOPORT, TEENSY_PIN13); +// } +// chThdSleepMilliseconds(100); +// } +// } + + + +/* Main thread + */ +int main(void) { + /* ChibiOS/RT init */ + halInit(); + chSysInit(); + + // TESTING + // chThdCreateStatic(waBlinkerThread, sizeof(waBlinkerThread), NORMALPRIO, blinkerThread, NULL); + + /* Init USB */ + init_usb_driver(&USB_DRIVER); + + /* init printf */ + init_printf(NULL,sendchar_pf); + + /* Wait until the USB is active */ + while(USB_DRIVER.state != USB_ACTIVE) + chThdSleepMilliseconds(50); + + print("USB configured.\n"); + + /* init TMK modules */ + keyboard_init(); + host_set_driver(&chibios_driver); + +#ifdef SLEEP_LED_ENABLE + sleep_led_init(); +#endif + + print("Keyboard start.\n"); + + /* Main loop */ + while(true) { + + if(USB_DRIVER.state == USB_SUSPENDED) { + print("[s]"); + while(USB_DRIVER.state == USB_SUSPENDED) { + /* Do this in the suspended state */ + suspend_power_down(); // on AVR this deep sleeps for 15ms + /* Remote wakeup */ + if((USB_DRIVER.status & 2) && suspend_wakeup_condition()) { + send_remote_wakeup(&USB_DRIVER); + } + } + /* Woken up */ + // variables has been already cleared by the wakeup hook + send_keyboard_report(); +#ifdef MOUSEKEY_ENABLE + mousekey_send(); +#endif /* MOUSEKEY_ENABLE */ + } + + keyboard_task(); + } +} diff --git a/protocol/chibios/usb_main.c b/protocol/chibios/usb_main.c new file mode 100644 index 00000000..d248b447 --- /dev/null +++ b/protocol/chibios/usb_main.c @@ -0,0 +1,1372 @@ +/* + * (c) 2015 flabberast + * + * Based on the following work: + * - Guillaume Duc's raw hid example (MIT License) + * https://github.com/guiduc/usb-hid-chibios-example + * - PJRC Teensy examples (MIT License) + * https://www.pjrc.com/teensy/usb_keyboard.html + * - hasu's TMK keyboard code (GPL v2 and some code Modified BSD) + * https://github.com/tmk/tmk_keyboard/ + * - ChibiOS demo code (Apache 2.0 License) + * http://www.chibios.org + * + * Since some GPL'd code is used, this work is licensed under + * GPL v2 or later. + */ + +#include "ch.h" +#include "hal.h" + +#include "usb_main.h" + +#include "host.h" +#include "debug.h" +#include "suspend.h" +#ifdef SLEEP_LED_ENABLE +#include "sleep_led.h" +#include "led.h" +#endif + +/* --------------------------------------------------------- + * Global interface variables and declarations + * --------------------------------------------------------- + */ + +uint8_t keyboard_idle = 0; +uint8_t keyboard_protocol = 1; +uint16_t keyboard_led_stats = 0; +volatile uint16_t keyboard_idle_count = 0; +static virtual_timer_t keyboard_idle_timer; +static void keyboard_idle_timer_cb(void *arg); +#ifdef NKRO_ENABLE +extern bool keyboard_nkro; +#endif /* NKRO_ENABLE */ + +report_keyboard_t keyboard_report_sent = {{0}}; +#ifdef MOUSE_ENABLE +report_mouse_t mouse_report_blank = {0}; +#endif /* MOUSE_ENABLE */ +#ifdef EXTRAKEY_ENABLE +uint8_t extra_report_blank[3] = {0}; +#endif /* EXTRAKEY_ENABLE */ + +#ifdef CONSOLE_ENABLE +/* The emission buffers queue */ +output_buffers_queue_t console_buf_queue; +static uint8_t console_queue_buffer[BQ_BUFFER_SIZE(CONSOLE_QUEUE_CAPACITY, CONSOLE_EPSIZE)]; + +static virtual_timer_t console_flush_timer; +void console_queue_onotify(io_buffers_queue_t *bqp); +static void console_flush_cb(void *arg); +#endif /* CONSOLE_ENABLE */ + +/* --------------------------------------------------------- + * Descriptors and USB driver objects + * --------------------------------------------------------- + */ + +/* HID specific constants */ +#define USB_DESCRIPTOR_HID 0x21 +#define USB_DESCRIPTOR_HID_REPORT 0x22 +#define HID_GET_REPORT 0x01 +#define HID_GET_IDLE 0x02 +#define HID_GET_PROTOCOL 0x03 +#define HID_SET_REPORT 0x09 +#define HID_SET_IDLE 0x0A +#define HID_SET_PROTOCOL 0x0B + +/* USB Device Descriptor */ +static const uint8_t usb_device_descriptor_data[] = { + USB_DESC_DEVICE(0x0200, // bcdUSB (1.1) + 0, // bDeviceClass (defined in later in interface) + 0, // bDeviceSubClass + 0, // bDeviceProtocol + 64, // bMaxPacketSize (64 bytes) (the driver didn't work with 32) + VENDOR_ID, // idVendor + PRODUCT_ID, // idProduct + DEVICE_VER, // bcdDevice + 1, // iManufacturer + 2, // iProduct + 3, // iSerialNumber + 1) // bNumConfigurations +}; + +/* Device Descriptor wrapper */ +static const USBDescriptor usb_device_descriptor = { + sizeof usb_device_descriptor_data, + usb_device_descriptor_data +}; + +/* + * HID Report Descriptor + * + * See "Device Class Definition for Human Interface Devices (HID)" + * (http://www.usb.org/developers/hidpage/HID1_11.pdf) for the + * detailed descrition of all the fields + */ + +/* Keyboard Protocol 1, HID 1.11 spec, Appendix B, page 59-60 */ +static const uint8_t keyboard_hid_report_desc_data[] = { + 0x05, 0x01, // Usage Page (Generic Desktop), + 0x09, 0x06, // Usage (Keyboard), + 0xA1, 0x01, // Collection (Application), + 0x75, 0x01, // Report Size (1), + 0x95, 0x08, // Report Count (8), + 0x05, 0x07, // Usage Page (Key Codes), + 0x19, 0xE0, // Usage Minimum (224), + 0x29, 0xE7, // Usage Maximum (231), + 0x15, 0x00, // Logical Minimum (0), + 0x25, 0x01, // Logical Maximum (1), + 0x81, 0x02, // Input (Data, Variable, Absolute), ;Modifier byte + 0x95, 0x01, // Report Count (1), + 0x75, 0x08, // Report Size (8), + 0x81, 0x03, // Input (Constant), ;Reserved byte + 0x95, 0x05, // Report Count (5), + 0x75, 0x01, // Report Size (1), + 0x05, 0x08, // Usage Page (LEDs), + 0x19, 0x01, // Usage Minimum (1), + 0x29, 0x05, // Usage Maximum (5), + 0x91, 0x02, // Output (Data, Variable, Absolute), ;LED report + 0x95, 0x01, // Report Count (1), + 0x75, 0x03, // Report Size (3), + 0x91, 0x03, // Output (Constant), ;LED report padding + 0x95, KBD_REPORT_KEYS, // Report Count (), + 0x75, 0x08, // Report Size (8), + 0x15, 0x00, // Logical Minimum (0), + 0x25, 0xFF, // Logical Maximum(255), + 0x05, 0x07, // Usage Page (Key Codes), + 0x19, 0x00, // Usage Minimum (0), + 0x29, 0xFF, // Usage Maximum (255), + 0x81, 0x00, // Input (Data, Array), + 0xc0 // End Collection +}; +/* wrapper */ +static const USBDescriptor keyboard_hid_report_descriptor = { + sizeof keyboard_hid_report_desc_data, + keyboard_hid_report_desc_data +}; + +#ifdef NKRO_ENABLE +static const uint8_t nkro_hid_report_desc_data[] = { + 0x05, 0x01, // Usage Page (Generic Desktop), + 0x09, 0x06, // Usage (Keyboard), + 0xA1, 0x01, // Collection (Application), + // bitmap of modifiers + 0x75, 0x01, // Report Size (1), + 0x95, 0x08, // Report Count (8), + 0x05, 0x07, // Usage Page (Key Codes), + 0x19, 0xE0, // Usage Minimum (224), + 0x29, 0xE7, // Usage Maximum (231), + 0x15, 0x00, // Logical Minimum (0), + 0x25, 0x01, // Logical Maximum (1), + 0x81, 0x02, // Input (Data, Variable, Absolute), ;Modifier byte + // LED output report + 0x95, 0x05, // Report Count (5), + 0x75, 0x01, // Report Size (1), + 0x05, 0x08, // Usage Page (LEDs), + 0x19, 0x01, // Usage Minimum (1), + 0x29, 0x05, // Usage Maximum (5), + 0x91, 0x02, // Output (Data, Variable, Absolute), + 0x95, 0x01, // Report Count (1), + 0x75, 0x03, // Report Size (3), + 0x91, 0x03, // Output (Constant), + // bitmap of keys + 0x95, NKRO_REPORT_KEYS * 8, // Report Count (), + 0x75, 0x01, // Report Size (1), + 0x15, 0x00, // Logical Minimum (0), + 0x25, 0x01, // Logical Maximum(1), + 0x05, 0x07, // Usage Page (Key Codes), + 0x19, 0x00, // Usage Minimum (0), + 0x29, NKRO_REPORT_KEYS * 8 - 1, // Usage Maximum (), + 0x81, 0x02, // Input (Data, Variable, Absolute), + 0xc0 // End Collection +}; +/* wrapper */ +static const USBDescriptor nkro_hid_report_descriptor = { + sizeof nkro_hid_report_desc_data, + nkro_hid_report_desc_data +}; +#endif /* NKRO_ENABLE */ + +#ifdef MOUSE_ENABLE +/* Mouse Protocol 1, HID 1.11 spec, Appendix B, page 59-60, with wheel extension + * http://www.microchip.com/forums/tm.aspx?high=&m=391435&mpage=1#391521 + * http://www.keil.com/forum/15671/ + * http://www.microsoft.com/whdc/device/input/wheel.mspx */ +static const uint8_t mouse_hid_report_desc_data[] = { + /* mouse */ + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x02, // USAGE (Mouse) + 0xa1, 0x01, // COLLECTION (Application) + //0x85, REPORT_ID_MOUSE, // REPORT_ID (1) + 0x09, 0x01, // USAGE (Pointer) + 0xa1, 0x00, // COLLECTION (Physical) + // ---------------------------- Buttons + 0x05, 0x09, // USAGE_PAGE (Button) + 0x19, 0x01, // USAGE_MINIMUM (Button 1) + 0x29, 0x05, // USAGE_MAXIMUM (Button 5) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x05, // REPORT_COUNT (5) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x75, 0x03, // REPORT_SIZE (3) + 0x95, 0x01, // REPORT_COUNT (1) + 0x81, 0x03, // INPUT (Cnst,Var,Abs) + // ---------------------------- X,Y position + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x30, // USAGE (X) + 0x09, 0x31, // USAGE (Y) + 0x15, 0x81, // LOGICAL_MINIMUM (-127) + 0x25, 0x7f, // LOGICAL_MAXIMUM (127) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x02, // REPORT_COUNT (2) + 0x81, 0x06, // INPUT (Data,Var,Rel) + // ---------------------------- Vertical wheel + 0x09, 0x38, // USAGE (Wheel) + 0x15, 0x81, // LOGICAL_MINIMUM (-127) + 0x25, 0x7f, // LOGICAL_MAXIMUM (127) + 0x35, 0x00, // PHYSICAL_MINIMUM (0) - reset physical + 0x45, 0x00, // PHYSICAL_MAXIMUM (0) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x01, // REPORT_COUNT (1) + 0x81, 0x06, // INPUT (Data,Var,Rel) + // ---------------------------- Horizontal wheel + 0x05, 0x0c, // USAGE_PAGE (Consumer Devices) + 0x0a, 0x38, 0x02, // USAGE (AC Pan) + 0x15, 0x81, // LOGICAL_MINIMUM (-127) + 0x25, 0x7f, // LOGICAL_MAXIMUM (127) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x01, // REPORT_COUNT (1) + 0x81, 0x06, // INPUT (Data,Var,Rel) + 0xc0, // END_COLLECTION + 0xc0, // END_COLLECTION +}; +/* wrapper */ +static const USBDescriptor mouse_hid_report_descriptor = { + sizeof mouse_hid_report_desc_data, + mouse_hid_report_desc_data +}; +#endif /* MOUSE_ENABLE */ + +#ifdef CONSOLE_ENABLE +static const uint8_t console_hid_report_desc_data[] = { + 0x06, 0x31, 0xFF, // Usage Page 0xFF31 (vendor defined) + 0x09, 0x74, // Usage 0x74 + 0xA1, 0x53, // Collection 0x53 + 0x75, 0x08, // report size = 8 bits + 0x15, 0x00, // logical minimum = 0 + 0x26, 0xFF, 0x00, // logical maximum = 255 + 0x95, CONSOLE_EPSIZE, // report count + 0x09, 0x75, // usage + 0x81, 0x02, // Input (array) + 0xC0 // end collection +}; +/* wrapper */ +static const USBDescriptor console_hid_report_descriptor = { + sizeof console_hid_report_desc_data, + console_hid_report_desc_data +}; +#endif /* CONSOLE_ENABLE */ + +#ifdef EXTRAKEY_ENABLE +/* audio controls & system controls + * http://www.microsoft.com/whdc/archive/w2kbd.mspx */ +static const uint8_t extra_hid_report_desc_data[] = { + /* system control */ + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x80, // USAGE (System Control) + 0xa1, 0x01, // COLLECTION (Application) + 0x85, REPORT_ID_SYSTEM, // REPORT_ID (2) + 0x15, 0x01, // LOGICAL_MINIMUM (0x1) + 0x25, 0xb7, // LOGICAL_MAXIMUM (0xb7) + 0x19, 0x01, // USAGE_MINIMUM (0x1) + 0x29, 0xb7, // USAGE_MAXIMUM (0xb7) + 0x75, 0x10, // REPORT_SIZE (16) + 0x95, 0x01, // REPORT_COUNT (1) + 0x81, 0x00, // INPUT (Data,Array,Abs) + 0xc0, // END_COLLECTION + /* consumer */ + 0x05, 0x0c, // USAGE_PAGE (Consumer Devices) + 0x09, 0x01, // USAGE (Consumer Control) + 0xa1, 0x01, // COLLECTION (Application) + 0x85, REPORT_ID_CONSUMER, // REPORT_ID (3) + 0x15, 0x01, // LOGICAL_MINIMUM (0x1) + 0x26, 0x9c, 0x02, // LOGICAL_MAXIMUM (0x29c) + 0x19, 0x01, // USAGE_MINIMUM (0x1) + 0x2a, 0x9c, 0x02, // USAGE_MAXIMUM (0x29c) + 0x75, 0x10, // REPORT_SIZE (16) + 0x95, 0x01, // REPORT_COUNT (1) + 0x81, 0x00, // INPUT (Data,Array,Abs) + 0xc0, // END_COLLECTION +}; +/* wrapper */ +static const USBDescriptor extra_hid_report_descriptor = { + sizeof extra_hid_report_desc_data, + extra_hid_report_desc_data +}; +#endif /* EXTRAKEY_ENABLE */ + + +/* + * Configuration Descriptor tree for a HID device + * + * The HID Specifications version 1.11 require the following order: + * - Configuration Descriptor + * - Interface Descriptor + * - HID Descriptor + * - Endpoints Descriptors + */ +#define KBD_HID_DESC_NUM 0 +#define KBD_HID_DESC_OFFSET (9 + (9 + 9 + 7) * KBD_HID_DESC_NUM + 9) + +#ifdef MOUSE_ENABLE +# define MOUSE_HID_DESC_NUM (KBD_HID_DESC_NUM + 1) +# define MOUSE_HID_DESC_OFFSET (9 + (9 + 9 + 7) * MOUSE_HID_DESC_NUM + 9) +#else /* MOUSE_ENABLE */ +# define MOUSE_HID_DESC_NUM (KBD_HID_DESC_NUM + 0) +#endif /* MOUSE_ENABLE */ + +#ifdef CONSOLE_ENABLE +#define CONSOLE_HID_DESC_NUM (MOUSE_HID_DESC_NUM + 1) +#define CONSOLE_HID_DESC_OFFSET (9 + (9 + 9 + 7) * CONSOLE_HID_DESC_NUM + 9) +#else /* CONSOLE_ENABLE */ +# define CONSOLE_HID_DESC_NUM (MOUSE_HID_DESC_NUM + 0) +#endif /* CONSOLE_ENABLE */ + +#ifdef EXTRAKEY_ENABLE +# define EXTRA_HID_DESC_NUM (CONSOLE_HID_DESC_NUM + 1) +# define EXTRA_HID_DESC_OFFSET (9 + (9 + 9 + 7) * EXTRA_HID_DESC_NUM + 9) +#else /* EXTRAKEY_ENABLE */ +# define EXTRA_HID_DESC_NUM (CONSOLE_HID_DESC_NUM + 0) +#endif /* EXTRAKEY_ENABLE */ + +#ifdef NKRO_ENABLE +# define NKRO_HID_DESC_NUM (EXTRA_HID_DESC_NUM + 1) +# define NKRO_HID_DESC_OFFSET (9 + (9 + 9 + 7) * EXTRA_HID_DESC_NUM + 9) +#else /* NKRO_ENABLE */ +# define NKRO_HID_DESC_NUM (EXTRA_HID_DESC_NUM + 0) +#endif /* NKRO_ENABLE */ + +#define NUM_INTERFACES (NKRO_HID_DESC_NUM + 1) +#define CONFIG1_DESC_SIZE (9 + (9 + 9 + 7) * NUM_INTERFACES) + +static const uint8_t hid_configuration_descriptor_data[] = { + /* Configuration Descriptor (9 bytes) USB spec 9.6.3, page 264-266, Table 9-10 */ + USB_DESC_CONFIGURATION(CONFIG1_DESC_SIZE, // wTotalLength + NUM_INTERFACES, // bNumInterfaces + 1, // bConfigurationValue + 0, // iConfiguration + 0xA0, // bmAttributes (RESERVED|REMOTEWAKEUP) + 50), // bMaxPower (50mA) + + /* Interface Descriptor (9 bytes) USB spec 9.6.5, page 267-269, Table 9-12 */ + USB_DESC_INTERFACE(KBD_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass: HID + 0x01, // bInterfaceSubClass: Boot + 0x01, // bInterfaceProtocol: Keyboard + 0), // iInterface + + /* HID descriptor (9 bytes) HID 1.11 spec, section 6.2.1 */ + USB_DESC_BYTE(9), // bLength + USB_DESC_BYTE(0x21), // bDescriptorType (HID class) + USB_DESC_BCD(0x0111), // bcdHID: HID version 1.11 + USB_DESC_BYTE(0), // bCountryCode + USB_DESC_BYTE(1), // bNumDescriptors + USB_DESC_BYTE(0x22), // bDescriptorType (report desc) + USB_DESC_WORD(sizeof(keyboard_hid_report_desc_data)), // wDescriptorLength + + /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */ + USB_DESC_ENDPOINT(KBD_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (Interrupt) + KBD_EPSIZE,// wMaxPacketSize + 10), // bInterval + + #ifdef MOUSE_ENABLE + /* Interface Descriptor (9 bytes) USB spec 9.6.5, page 267-269, Table 9-12 */ + USB_DESC_INTERFACE(MOUSE_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass (0x03 = HID) + // ThinkPad T23 BIOS doesn't work with boot mouse. + 0x00, // bInterfaceSubClass (0x01 = Boot) + 0x00, // bInterfaceProtocol (0x02 = Mouse) + /* + 0x01, // bInterfaceSubClass (0x01 = Boot) + 0x02, // bInterfaceProtocol (0x02 = Mouse) + */ + 0), // iInterface + + /* HID descriptor (9 bytes) HID 1.11 spec, section 6.2.1 */ + USB_DESC_BYTE(9), // bLength + USB_DESC_BYTE(0x21), // bDescriptorType (HID class) + USB_DESC_BCD(0x0111), // bcdHID: HID version 1.11 + USB_DESC_BYTE(0), // bCountryCode + USB_DESC_BYTE(1), // bNumDescriptors + USB_DESC_BYTE(0x22), // bDescriptorType (report desc) + USB_DESC_WORD(sizeof(mouse_hid_report_desc_data)), // wDescriptorLength + + /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */ + USB_DESC_ENDPOINT(MOUSE_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (Interrupt) + MOUSE_EPSIZE, // wMaxPacketSize + 1), // bInterval + #endif /* MOUSE_ENABLE */ + + #ifdef CONSOLE_ENABLE + /* Interface Descriptor (9 bytes) USB spec 9.6.5, page 267-269, Table 9-12 */ + USB_DESC_INTERFACE(CONSOLE_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass: HID + 0x00, // bInterfaceSubClass: None + 0x00, // bInterfaceProtocol: None + 0), // iInterface + + /* HID descriptor (9 bytes) HID 1.11 spec, section 6.2.1 */ + USB_DESC_BYTE(9), // bLength + USB_DESC_BYTE(0x21), // bDescriptorType (HID class) + USB_DESC_BCD(0x0111), // bcdHID: HID version 1.11 + USB_DESC_BYTE(0), // bCountryCode + USB_DESC_BYTE(1), // bNumDescriptors + USB_DESC_BYTE(0x22), // bDescriptorType (report desc) + USB_DESC_WORD(sizeof(console_hid_report_desc_data)), // wDescriptorLength + + /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */ + USB_DESC_ENDPOINT(CONSOLE_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (Interrupt) + CONSOLE_EPSIZE, // wMaxPacketSize + 1), // bInterval + #endif /* CONSOLE_ENABLE */ + + #ifdef EXTRAKEY_ENABLE + /* Interface Descriptor (9 bytes) USB spec 9.6.5, page 267-269, Table 9-12 */ + USB_DESC_INTERFACE(EXTRA_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass: HID + 0x00, // bInterfaceSubClass: None + 0x00, // bInterfaceProtocol: None + 0), // iInterface + + /* HID descriptor (9 bytes) HID 1.11 spec, section 6.2.1 */ + USB_DESC_BYTE(9), // bLength + USB_DESC_BYTE(0x21), // bDescriptorType (HID class) + USB_DESC_BCD(0x0111), // bcdHID: HID version 1.11 + USB_DESC_BYTE(0), // bCountryCode + USB_DESC_BYTE(1), // bNumDescriptors + USB_DESC_BYTE(0x22), // bDescriptorType (report desc) + USB_DESC_WORD(sizeof(extra_hid_report_desc_data)), // wDescriptorLength + + /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */ + USB_DESC_ENDPOINT(EXTRA_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (Interrupt) + EXTRA_EPSIZE, // wMaxPacketSize + 10), // bInterval + #endif /* EXTRAKEY_ENABLE */ + + #ifdef NKRO_ENABLE + /* Interface Descriptor (9 bytes) USB spec 9.6.5, page 267-269, Table 9-12 */ + USB_DESC_INTERFACE(NKRO_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass: HID + 0x00, // bInterfaceSubClass: None + 0x00, // bInterfaceProtocol: None + 0), // iInterface + + /* HID descriptor (9 bytes) HID 1.11 spec, section 6.2.1 */ + USB_DESC_BYTE(9), // bLength + USB_DESC_BYTE(0x21), // bDescriptorType (HID class) + USB_DESC_BCD(0x0111), // bcdHID: HID version 1.11 + USB_DESC_BYTE(0), // bCountryCode + USB_DESC_BYTE(1), // bNumDescriptors + USB_DESC_BYTE(0x22), // bDescriptorType (report desc) + USB_DESC_WORD(sizeof(nkro_hid_report_desc_data)), // wDescriptorLength + + /* Endpoint Descriptor (7 bytes) USB spec 9.6.6, page 269-271, Table 9-13 */ + USB_DESC_ENDPOINT(NKRO_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (Interrupt) + NKRO_EPSIZE, // wMaxPacketSize + 1), // bInterval + #endif /* NKRO_ENABLE */ +}; + +/* Configuration Descriptor wrapper */ +static const USBDescriptor hid_configuration_descriptor = { + sizeof hid_configuration_descriptor_data, + hid_configuration_descriptor_data +}; + +/* wrappers */ +#define HID_DESCRIPTOR_SIZE 9 +static const USBDescriptor keyboard_hid_descriptor = { + HID_DESCRIPTOR_SIZE, + &hid_configuration_descriptor_data[KBD_HID_DESC_OFFSET] +}; +#ifdef MOUSE_ENABLE +static const USBDescriptor mouse_hid_descriptor = { + HID_DESCRIPTOR_SIZE, + &hid_configuration_descriptor_data[MOUSE_HID_DESC_OFFSET] +}; +#endif /* MOUSE_ENABLE */ +#ifdef CONSOLE_ENABLE +static const USBDescriptor console_hid_descriptor = { + HID_DESCRIPTOR_SIZE, + &hid_configuration_descriptor_data[CONSOLE_HID_DESC_OFFSET] +}; +#endif /* CONSOLE_ENABLE */ +#ifdef EXTRAKEY_ENABLE +static const USBDescriptor extra_hid_descriptor = { + HID_DESCRIPTOR_SIZE, + &hid_configuration_descriptor_data[EXTRA_HID_DESC_OFFSET] +}; +#endif /* EXTRAKEY_ENABLE */ +#ifdef NKRO_ENABLE +static const USBDescriptor nkro_hid_descriptor = { + HID_DESCRIPTOR_SIZE, + &hid_configuration_descriptor_data[NKRO_HID_DESC_OFFSET] +}; +#endif /* NKRO_ENABLE */ + + +/* U.S. English language identifier */ +static const uint8_t usb_string_langid[] = { + USB_DESC_BYTE(4), // bLength + USB_DESC_BYTE(USB_DESCRIPTOR_STRING), // bDescriptorType + USB_DESC_WORD(0x0409) // wLANGID (U.S. English) +}; + +/* ugly ugly hack */ +#define PP_NARG(...) \ + PP_NARG_(__VA_ARGS__,PP_RSEQ_N()) +#define PP_NARG_(...) \ + PP_ARG_N(__VA_ARGS__) +#define PP_ARG_N( \ + _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ + _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ + _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ + _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ + _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ + _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ + _61,_62,_63,N,...) N +#define PP_RSEQ_N() \ + 63,62,61,60, \ + 59,58,57,56,55,54,53,52,51,50, \ + 49,48,47,46,45,44,43,42,41,40, \ + 39,38,37,36,35,34,33,32,31,30, \ + 29,28,27,26,25,24,23,22,21,20, \ + 19,18,17,16,15,14,13,12,11,10, \ + 9,8,7,6,5,4,3,2,1,0 + +/* Vendor string = manufacturer */ +static const uint8_t usb_string_vendor[] = { + USB_DESC_BYTE(PP_NARG(USBSTR_MANUFACTURER)+2), // bLength + USB_DESC_BYTE(USB_DESCRIPTOR_STRING), // bDescriptorType + USBSTR_MANUFACTURER +}; + +/* Device Description string = product */ +static const uint8_t usb_string_description[] = { + USB_DESC_BYTE(PP_NARG(USBSTR_PRODUCT)+2), // bLength + USB_DESC_BYTE(USB_DESCRIPTOR_STRING), // bDescriptorType + USBSTR_PRODUCT +}; + +/* Serial Number string (will be filled by the function init_usb_serial_string) */ +static uint8_t usb_string_serial[] = { + USB_DESC_BYTE(22), // bLength + USB_DESC_BYTE(USB_DESCRIPTOR_STRING), // bDescriptorType + '0', 0, 'x', 0, 'D', 0, 'E', 0, 'A', 0, 'D', 0, 'B', 0, 'E', 0, 'E', 0, 'F', 0 +}; + +/* Strings wrappers array */ +static const USBDescriptor usb_strings[] = { + { sizeof usb_string_langid, usb_string_langid } + , + { sizeof usb_string_vendor, usb_string_vendor } + , + { sizeof usb_string_description, usb_string_description } + , + { sizeof usb_string_serial, usb_string_serial } +}; + +/* + * Handles the GET_DESCRIPTOR callback + * + * Returns the proper descriptor + */ +static const USBDescriptor *usb_get_descriptor_cb(USBDriver *usbp, uint8_t dtype, uint8_t dindex, uint16_t lang) { + (void)usbp; + (void)lang; + switch(dtype) { + /* Generic descriptors */ + case USB_DESCRIPTOR_DEVICE: /* Device Descriptor */ + return &usb_device_descriptor; + + case USB_DESCRIPTOR_CONFIGURATION: /* Configuration Descriptor */ + return &hid_configuration_descriptor; + + case USB_DESCRIPTOR_STRING: /* Strings */ + if(dindex < 4) + return &usb_strings[dindex]; + break; + + /* HID specific descriptors */ + case USB_DESCRIPTOR_HID: /* HID Descriptors */ + switch(lang) { /* yea, poor label, it's actually wIndex from the setup packet */ + case KBD_INTERFACE: + return &keyboard_hid_descriptor; + +#ifdef MOUSE_ENABLE + case MOUSE_INTERFACE: + return &mouse_hid_descriptor; +#endif /* MOUSE_ENABLE */ +#ifdef CONSOLE_ENABLE + case CONSOLE_INTERFACE: + return &console_hid_descriptor; +#endif /* CONSOLE_ENABLE */ +#ifdef EXTRAKEY_ENABLE + case EXTRA_INTERFACE: + return &extra_hid_descriptor; +#endif /* EXTRAKEY_ENABLE */ +#ifdef NKRO_ENABLE + case NKRO_INTERFACE: + return &nkro_hid_descriptor; +#endif /* NKRO_ENABLE */ + } + + case USB_DESCRIPTOR_HID_REPORT: /* HID Report Descriptor */ + switch(lang) { + case KBD_INTERFACE: + return &keyboard_hid_report_descriptor; + +#ifdef MOUSE_ENABLE + case MOUSE_INTERFACE: + return &mouse_hid_report_descriptor; +#endif /* MOUSE_ENABLE */ +#ifdef CONSOLE_ENABLE + case CONSOLE_INTERFACE: + return &console_hid_report_descriptor; +#endif /* CONSOLE_ENABLE */ +#ifdef EXTRAKEY_ENABLE + case EXTRA_INTERFACE: + return &extra_hid_report_descriptor; +#endif /* EXTRAKEY_ENABLE */ +#ifdef NKRO_ENABLE + case NKRO_INTERFACE: + return &nkro_hid_report_descriptor; +#endif /* NKRO_ENABLE */ + } + } + return NULL; +} + +/* keyboard endpoint state structure */ +static USBInEndpointState kbd_ep_state; +/* keyboard endpoint initialization structure (IN) */ +static const USBEndpointConfig kbd_ep_config = { + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ + NULL, /* SETUP packet notification callback */ + kbd_in_cb, /* IN notification callback */ + NULL, /* OUT notification callback */ + KBD_EPSIZE, /* IN maximum packet size */ + 0, /* OUT maximum packet size */ + &kbd_ep_state, /* IN Endpoint state */ + NULL, /* OUT endpoint state */ + 2, /* IN multiplier */ + NULL /* SETUP buffer (not a SETUP endpoint) */ +}; + +#ifdef MOUSE_ENABLE +/* mouse endpoint state structure */ +static USBInEndpointState mouse_ep_state; + +/* mouse endpoint initialization structure (IN) */ +static const USBEndpointConfig mouse_ep_config = { + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ + NULL, /* SETUP packet notification callback */ + mouse_in_cb, /* IN notification callback */ + NULL, /* OUT notification callback */ + MOUSE_EPSIZE, /* IN maximum packet size */ + 0, /* OUT maximum packet size */ + &mouse_ep_state, /* IN Endpoint state */ + NULL, /* OUT endpoint state */ + 2, /* IN multiplier */ + NULL /* SETUP buffer (not a SETUP endpoint) */ +}; +#endif /* MOUSE_ENABLE */ + +#ifdef CONSOLE_ENABLE +/* console endpoint state structure */ +static USBInEndpointState console_ep_state; + +/* console endpoint initialization structure (IN) */ +static const USBEndpointConfig console_ep_config = { + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ + NULL, /* SETUP packet notification callback */ + console_in_cb, /* IN notification callback */ + NULL, /* OUT notification callback */ + CONSOLE_EPSIZE, /* IN maximum packet size */ + 0, /* OUT maximum packet size */ + &console_ep_state, /* IN Endpoint state */ + NULL, /* OUT endpoint state */ + 2, /* IN multiplier */ + NULL /* SETUP buffer (not a SETUP endpoint) */ +}; +#endif /* CONSOLE_ENABLE */ + +#ifdef EXTRAKEY_ENABLE +/* extrakey endpoint state structure */ +static USBInEndpointState extra_ep_state; + +/* extrakey endpoint initialization structure (IN) */ +static const USBEndpointConfig extra_ep_config = { + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ + NULL, /* SETUP packet notification callback */ + extra_in_cb, /* IN notification callback */ + NULL, /* OUT notification callback */ + EXTRA_EPSIZE, /* IN maximum packet size */ + 0, /* OUT maximum packet size */ + &extra_ep_state, /* IN Endpoint state */ + NULL, /* OUT endpoint state */ + 2, /* IN multiplier */ + NULL /* SETUP buffer (not a SETUP endpoint) */ +}; +#endif /* EXTRAKEY_ENABLE */ + +#ifdef NKRO_ENABLE +/* nkro endpoint state structure */ +static USBInEndpointState nkro_ep_state; + +/* nkro endpoint initialization structure (IN) */ +static const USBEndpointConfig nkro_ep_config = { + USB_EP_MODE_TYPE_INTR, /* Interrupt EP */ + NULL, /* SETUP packet notification callback */ + nkro_in_cb, /* IN notification callback */ + NULL, /* OUT notification callback */ + NKRO_EPSIZE, /* IN maximum packet size */ + 0, /* OUT maximum packet size */ + &nkro_ep_state, /* IN Endpoint state */ + NULL, /* OUT endpoint state */ + 2, /* IN multiplier */ + NULL /* SETUP buffer (not a SETUP endpoint) */ +}; +#endif /* NKRO_ENABLE */ + +/* --------------------------------------------------------- + * USB driver functions + * --------------------------------------------------------- + */ + +/* Handles the USB driver global events + * TODO: maybe disable some things when connection is lost? */ +static void usb_event_cb(USBDriver *usbp, usbevent_t event) { + switch(event) { + case USB_EVENT_RESET: + //TODO: from ISR! print("[R]"); + return; + + case USB_EVENT_ADDRESS: + return; + + case USB_EVENT_CONFIGURED: + osalSysLockFromISR(); + /* Enable the endpoints specified into the configuration. */ + usbInitEndpointI(usbp, KBD_ENDPOINT, &kbd_ep_config); +#ifdef MOUSE_ENABLE + usbInitEndpointI(usbp, MOUSE_ENDPOINT, &mouse_ep_config); +#endif /* MOUSE_ENABLE */ +#ifdef CONSOLE_ENABLE + usbInitEndpointI(usbp, CONSOLE_ENDPOINT, &console_ep_config); + /* don't need to start the flush timer, it starts from console_in_cb automatically */ +#endif /* CONSOLE_ENABLE */ +#ifdef EXTRAKEY_ENABLE + usbInitEndpointI(usbp, EXTRA_ENDPOINT, &extra_ep_config); +#endif /* EXTRAKEY_ENABLE */ +#ifdef NKRO_ENABLE + usbInitEndpointI(usbp, NKRO_ENDPOINT, &nkro_ep_config); +#endif /* NKRO_ENABLE */ + osalSysUnlockFromISR(); + return; + + case USB_EVENT_SUSPEND: + //TODO: from ISR! print("[S]"); +#ifdef SLEEP_LED_ENABLE + sleep_led_enable(); +#endif /* SLEEP_LED_ENABLE */ + return; + + case USB_EVENT_WAKEUP: + //TODO: from ISR! print("[W]"); + suspend_wakeup_init(); +#ifdef SLEEP_LED_ENABLE + sleep_led_disable(); + // NOTE: converters may not accept this + led_set(host_keyboard_leds()); +#endif /* SLEEP_LED_ENABLE */ + return; + + case USB_EVENT_STALLED: + return; + } +} + +/* Function used locally in os/hal/src/usb.c for getting descriptors + * need it here for HID descriptor */ +static uint16_t get_hword(uint8_t *p) { + uint16_t hw; + + hw = (uint16_t)*p++; + hw |= (uint16_t)*p << 8U; + return hw; +} + +/* + * Appendix G: HID Request Support Requirements + * + * The following table enumerates the requests that need to be supported by various types of HID class devices. + * Device type GetReport SetReport GetIdle SetIdle GetProtocol SetProtocol + * ------------------------------------------------------------------------------------------ + * Boot Mouse Required Optional Optional Optional Required Required + * Non-Boot Mouse Required Optional Optional Optional Optional Optional + * Boot Keyboard Required Optional Required Required Required Required + * Non-Boot Keybrd Required Optional Required Required Optional Optional + * Other Device Required Optional Optional Optional Optional Optional + */ + +/* Callback for SETUP request on the endpoint 0 (control) */ +static bool usb_request_hook_cb(USBDriver *usbp) { + const USBDescriptor *dp; + + /* usbp->setup fields: + * 0: bmRequestType (bitmask) + * 1: bRequest + * 2,3: (LSB,MSB) wValue + * 4,5: (LSB,MSB) wIndex + * 6,7: (LSB,MSB) wLength (number of bytes to transfer if there is a data phase) */ + + /* Handle HID class specific requests */ + if(((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) && + ((usbp->setup[0] & USB_RTYPE_RECIPIENT_MASK) == USB_RTYPE_RECIPIENT_INTERFACE)) { + switch(usbp->setup[0] & USB_RTYPE_DIR_MASK) { + case USB_RTYPE_DIR_DEV2HOST: + switch(usbp->setup[1]) { /* bRequest */ + case HID_GET_REPORT: + switch(usbp->setup[4]) { /* LSB(wIndex) (check MSB==0?) */ + case KBD_INTERFACE: +#ifdef NKRO_ENABLE + case NKRO_INTERFACE: +#endif /* NKRO_ENABLE */ + usbSetupTransfer(usbp, (uint8_t *)&keyboard_report_sent, sizeof(keyboard_report_sent), NULL); + return TRUE; + break; + +#ifdef MOUSE_ENABLE + case MOUSE_INTERFACE: + usbSetupTransfer(usbp, (uint8_t *)&mouse_report_blank, sizeof(mouse_report_blank), NULL); + return TRUE; + break; +#endif /* MOUSE_ENABLE */ + +#ifdef CONSOLE_ENABLE + case CONSOLE_INTERFACE: + usbSetupTransfer(usbp, console_queue_buffer, CONSOLE_EPSIZE, NULL); + return TRUE; + break; +#endif /* CONSOLE_ENABLE */ + +#ifdef EXTRAKEY_ENABLE + case EXTRA_INTERFACE: + if(usbp->setup[3] == 1) { /* MSB(wValue) [Report Type] == 1 [Input Report] */ + switch(usbp->setup[2]) { /* LSB(wValue) [Report ID] */ + case REPORT_ID_SYSTEM: + extra_report_blank[0] = REPORT_ID_SYSTEM; + usbSetupTransfer(usbp, (uint8_t *)extra_report_blank, sizeof(extra_report_blank), NULL); + return TRUE; + break; + case REPORT_ID_CONSUMER: + extra_report_blank[0] = REPORT_ID_CONSUMER; + usbSetupTransfer(usbp, (uint8_t *)extra_report_blank, sizeof(extra_report_blank), NULL); + return TRUE; + break; + default: + return FALSE; + } + } else { + return FALSE; + } + break; +#endif /* EXTRAKEY_ENABLE */ + + default: + usbSetupTransfer(usbp, NULL, 0, NULL); + return TRUE; + break; + } + break; + + case HID_GET_PROTOCOL: + if((usbp->setup[4] == KBD_INTERFACE) && (usbp->setup[5] == 0)) { /* wIndex */ + usbSetupTransfer(usbp, &keyboard_protocol, 1, NULL); + return TRUE; + } + break; + + case HID_GET_IDLE: + usbSetupTransfer(usbp, &keyboard_idle, 1, NULL); + return TRUE; + break; + } + break; + + case USB_RTYPE_DIR_HOST2DEV: + switch(usbp->setup[1]) { /* bRequest */ + case HID_SET_REPORT: + switch(usbp->setup[4]) { /* LSB(wIndex) (check MSB==0 and wLength==1?) */ + case KBD_INTERFACE: +#ifdef NKRO_ENABLE + case NKRO_INTERFACE: +#endif /* NKRO_ENABLE */ + /* keyboard_led_stats = + * keyboard_led_stats needs be word (or dword), otherwise we get an exception on F0 */ + usbSetupTransfer(usbp, (uint8_t *)&keyboard_led_stats, 1, NULL); + return TRUE; + break; + } + break; + + case HID_SET_PROTOCOL: + if((usbp->setup[4] == KBD_INTERFACE) && (usbp->setup[5] == 0)) { /* wIndex */ + keyboard_protocol = ((usbp->setup[2]) != 0x00); /* LSB(wValue) */ +#ifdef NKRO_ENABLE + keyboard_nkro = !!keyboard_protocol; + if(!keyboard_nkro && keyboard_idle) { +#else /* NKRO_ENABLE */ + if(keyboard_idle) { +#endif /* NKRO_ENABLE */ + /* arm the idle timer if boot protocol & idle */ + osalSysLockFromISR(); + chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); + osalSysUnlockFromISR(); + } + } + usbSetupTransfer(usbp, NULL, 0, NULL); + return TRUE; + break; + + case HID_SET_IDLE: + keyboard_idle = usbp->setup[3]; /* MSB(wValue) */ + /* arm the timer */ +#ifdef NKRO_ENABLE + if(!keyboard_nkro && keyboard_idle) { +#else /* NKRO_ENABLE */ + if(keyboard_idle) { +#endif /* NKRO_ENABLE */ + osalSysLockFromISR(); + chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); + osalSysUnlockFromISR(); + } + usbSetupTransfer(usbp, NULL, 0, NULL); + return TRUE; + break; + } + break; + } + } + + /* Handle the Get_Descriptor Request for HID class (not handled by the default hook) */ + if((usbp->setup[0] == 0x81) && (usbp->setup[1] == USB_REQ_GET_DESCRIPTOR)) { + dp = usbp->config->get_descriptor_cb(usbp, usbp->setup[3], usbp->setup[2], get_hword(&usbp->setup[4])); + if(dp == NULL) + return FALSE; + usbSetupTransfer(usbp, (uint8_t *)dp->ud_string, dp->ud_size, NULL); + return TRUE; + } + + return FALSE; +} + +/* Start-of-frame callback */ +static void usb_sof_cb(USBDriver *usbp) { + kbd_sof_cb(usbp); +} + + +/* USB driver configuration */ +static const USBConfig usbcfg = { + usb_event_cb, /* USB events callback */ + usb_get_descriptor_cb, /* Device GET_DESCRIPTOR request callback */ + usb_request_hook_cb, /* Requests hook callback */ + usb_sof_cb /* Start Of Frame callback */ +}; + +/* + * Initialize the USB driver + */ +void init_usb_driver(USBDriver *usbp) { + /* + * Activates the USB driver and then the USB bus pull-up on D+. + * Note, a delay is inserted in order to not have to disconnect the cable + * after a reset. + */ + usbDisconnectBus(usbp); + chThdSleepMilliseconds(1500); + usbStart(usbp, &usbcfg); + usbConnectBus(usbp); + + chVTObjectInit(&keyboard_idle_timer); +#ifdef CONSOLE_ENABLE + obqObjectInit(&console_buf_queue, console_queue_buffer, CONSOLE_EPSIZE, CONSOLE_QUEUE_CAPACITY, console_queue_onotify, (void*)usbp); + chVTObjectInit(&console_flush_timer); +#endif +} + +/* + * Send remote wakeup packet + * Note: should not be called from ISR + */ +void send_remote_wakeup(USBDriver *usbp) { + (void)usbp; +#if defined(K20x) || defined(KL2x) +#if KINETIS_USB_USE_USB0 + USB0->CTL |= USBx_CTL_RESUME; + chThdSleepMilliseconds(15); + USB0->CTL &= ~USBx_CTL_RESUME; +#endif /* KINETIS_USB_USE_USB0 */ +#elif defined(STM32F0XX) || defined(STM32F1XX) /* K20x || KL2x */ + STM32_USB->CNTR |= CNTR_RESUME; + chThdSleepMilliseconds(15); + STM32_USB->CNTR &= ~CNTR_RESUME; +#else /* STM32F0XX || STM32F1XX */ +#warning Sending remote wakeup packet not implemented for your platform. +#endif /* K20x || KL2x */ +} + +/* --------------------------------------------------------- + * Keyboard functions + * --------------------------------------------------------- + */ + +/* keyboard IN callback hander (a kbd report has made it IN) */ +void kbd_in_cb(USBDriver *usbp, usbep_t ep) { + /* STUB */ + (void)usbp; + (void)ep; +} + +#ifdef NKRO_ENABLE +/* nkro IN callback hander (a nkro report has made it IN) */ +void nkro_in_cb(USBDriver *usbp, usbep_t ep) { + /* STUB */ + (void)usbp; + (void)ep; +} +#endif /* NKRO_ENABLE */ + +/* start-of-frame handler + * TODO: i guess it would be better to re-implement using timers, + * so that this is not going to have to be checked every 1ms */ +void kbd_sof_cb(USBDriver *usbp) { + (void)usbp; +} + +/* Idle requests timer code + * callback (called from ISR, unlocked state) */ +static void keyboard_idle_timer_cb(void *arg) { + USBDriver *usbp = (USBDriver *)arg; + + osalSysLockFromISR(); + + /* check that the states of things are as they're supposed to */ + if(usbGetDriverStateI(usbp) != USB_ACTIVE) { + /* do not rearm the timer, should be enabled on IDLE request */ + osalSysUnlockFromISR(); + return; + } + +#ifdef NKRO_ENABLE + if(!keyboard_nkro && keyboard_idle) { +#else /* NKRO_ENABLE */ + if(keyboard_idle) { +#endif /* NKRO_ENABLE */ + /* TODO: are we sure we want the KBD_ENDPOINT? */ + if(!usbGetTransmitStatusI(usbp, KBD_ENDPOINT)) { + usbStartTransmitI(usbp, KBD_ENDPOINT, (uint8_t *)&keyboard_report_sent, KBD_EPSIZE); + } + /* rearm the timer */ + chVTSetI(&keyboard_idle_timer, 4*MS2ST(keyboard_idle), keyboard_idle_timer_cb, (void *)usbp); + } + + /* do not rearm the timer if the condition above fails + * it should be enabled again on either IDLE or SET_PROTOCOL requests */ + osalSysUnlockFromISR(); +} + +/* LED status */ +uint8_t keyboard_leds(void) { + return (uint8_t)(keyboard_led_stats & 0xFF); +} + +/* prepare and start sending a report IN + * not callable from ISR or locked state */ +void send_keyboard(report_keyboard_t *report) { + osalSysLock(); + if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + osalSysUnlock(); + return; + } + osalSysUnlock(); + +#ifdef NKRO_ENABLE + if(keyboard_nkro) { /* NKRO protocol */ + /* need to wait until the previous packet has made it through */ + /* can rewrite this using the synchronous API, then would wait + * until *after* the packet has been transmitted. I think + * this is more efficient */ + /* busy wait, should be short and not very common */ + osalSysLock(); + if(usbGetTransmitStatusI(&USB_DRIVER, NKRO_ENDPOINT)) { + /* Need to either suspend, or loop and call unlock/lock during + * every iteration - otherwise the system will remain locked, + * no interrupts served, so USB not going through as well. + * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ + osalThreadSuspendS(&(&USB_DRIVER)->epc[NKRO_ENDPOINT]->in_state->thread); + } + usbStartTransmitI(&USB_DRIVER, NKRO_ENDPOINT, (uint8_t *)report, sizeof(report_keyboard_t)); + osalSysUnlock(); + } else +#endif /* NKRO_ENABLE */ + { /* boot protocol */ + /* need to wait until the previous packet has made it through */ + /* busy wait, should be short and not very common */ + osalSysLock(); + if(usbGetTransmitStatusI(&USB_DRIVER, KBD_ENDPOINT)) { + /* Need to either suspend, or loop and call unlock/lock during + * every iteration - otherwise the system will remain locked, + * no interrupts served, so USB not going through as well. + * Note: for suspend, need USB_USE_WAIT == TRUE in halconf.h */ + osalThreadSuspendS(&(&USB_DRIVER)->epc[KBD_ENDPOINT]->in_state->thread); + } + usbStartTransmitI(&USB_DRIVER, KBD_ENDPOINT, (uint8_t *)report, KBD_EPSIZE); + osalSysUnlock(); + } + keyboard_report_sent = *report; +} + +/* --------------------------------------------------------- + * Mouse functions + * --------------------------------------------------------- + */ + +#ifdef MOUSE_ENABLE + +/* mouse IN callback hander (a mouse report has made it IN) */ +void mouse_in_cb(USBDriver *usbp, usbep_t ep) { + (void)usbp; + (void)ep; +} + +void send_mouse(report_mouse_t *report) { + osalSysLock(); + if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + osalSysUnlock(); + return; + } + osalSysUnlock(); + + /* TODO: LUFA manually waits for the endpoint to become ready + * for about 10ms for mouse, kbd, system; 1ms for nkro + * is this really needed? + */ + + osalSysLock(); + usbStartTransmitI(&USB_DRIVER, MOUSE_ENDPOINT, (uint8_t *)report, sizeof(report_mouse_t)); + osalSysUnlock(); +} + +#else /* MOUSE_ENABLE */ +void send_mouse(report_mouse_t *report) { + (void)report; +} +#endif /* MOUSE_ENABLE */ + +/* --------------------------------------------------------- + * Extrakey functions + * --------------------------------------------------------- + */ + +#ifdef EXTRAKEY_ENABLE + +/* extrakey IN callback hander */ +void extra_in_cb(USBDriver *usbp, usbep_t ep) { + /* STUB */ + (void)usbp; + (void)ep; +} + +static void send_extra_report(uint8_t report_id, uint16_t data) { + osalSysLock(); + if(usbGetDriverStateI(&USB_DRIVER) != USB_ACTIVE) { + osalSysUnlock(); + return; + } + + report_extra_t report = { + .report_id = report_id, + .usage = data + }; + + usbStartTransmitI(&USB_DRIVER, EXTRA_ENDPOINT, (uint8_t *)&report, sizeof(report_extra_t)); + osalSysUnlock(); +} + +void send_system(uint16_t data) { + send_extra_report(REPORT_ID_SYSTEM, data); +} + +void send_consumer(uint16_t data) { + send_extra_report(REPORT_ID_CONSUMER, data); +} + +#else /* EXTRAKEY_ENABLE */ +void send_system(uint16_t data) { + (void)data; +} +void send_consumer(uint16_t data) { + (void)data; +} +#endif /* EXTRAKEY_ENABLE */ + +/* --------------------------------------------------------- + * Console functions + * --------------------------------------------------------- + */ + +#ifdef CONSOLE_ENABLE + +/* console IN callback hander */ +void console_in_cb(USBDriver *usbp, usbep_t ep) { + (void)ep; /* should have ep == CONSOLE_ENDPOINT, so use that to save time/space */ + uint8_t *buf; + size_t n; + + osalSysLockFromISR(); + + /* rearm the timer */ + chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, (void *)usbp); + + /* Freeing the buffer just transmitted, if it was not a zero size packet.*/ + if (usbp->epc[CONSOLE_ENDPOINT]->in_state->txsize > 0U) { + obqReleaseEmptyBufferI(&console_buf_queue); + } + + /* Checking if there is a buffer ready for transmission.*/ + buf = obqGetFullBufferI(&console_buf_queue, &n); + + if (buf != NULL) { + /* The endpoint cannot be busy, we are in the context of the callback, + so it is safe to transmit without a check.*/ + /* Should have n == CONSOLE_EPSIZE; check it? */ + usbStartTransmitI(usbp, CONSOLE_ENDPOINT, buf, CONSOLE_EPSIZE); + } else { + /* Nothing to transmit.*/ + } + + osalSysUnlockFromISR(); +} + +/* Callback when data is inserted into the output queue + * Called from a locked state */ +void console_queue_onotify(io_buffers_queue_t *bqp) { + size_t n; + USBDriver *usbp = bqGetLinkX(bqp); + + if(usbGetDriverStateI(usbp) != USB_ACTIVE) + return; + + /* Checking if there is already a transaction ongoing on the endpoint.*/ + if (!usbGetTransmitStatusI(usbp, CONSOLE_ENDPOINT)) { + /* Trying to get a full buffer.*/ + uint8_t *buf = obqGetFullBufferI(&console_buf_queue, &n); + if (buf != NULL) { + /* Buffer found, starting a new transaction.*/ + /* Should have n == CONSOLE_EPSIZE; check this? */ + usbStartTransmitI(usbp, CONSOLE_ENDPOINT, buf, CONSOLE_EPSIZE); + } + } +} + +/* Flush timer code + * callback (called from ISR, unlocked state) */ +static void console_flush_cb(void *arg) { + USBDriver *usbp = (USBDriver *)arg; + osalSysLockFromISR(); + + /* check that the states of things are as they're supposed to */ + if(usbGetDriverStateI(usbp) != USB_ACTIVE) { + /* rearm the timer */ + chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, (void *)usbp); + osalSysUnlockFromISR(); + return; + } + + /* If there is already a transaction ongoing then another one cannot be + started.*/ + if (usbGetTransmitStatusI(usbp, CONSOLE_ENDPOINT)) { + /* rearm the timer */ + chVTSetI(&console_flush_timer, MS2ST(CONSOLE_FLUSH_MS), console_flush_cb, (void *)usbp); + osalSysUnlockFromISR(); + return; + } + + /* Checking if there only a buffer partially filled, if so then it is + enforced in the queue and transmitted.*/ + if(obqTryFlushI(&console_buf_queue)) { + size_t n,i; + uint8_t *buf = obqGetFullBufferI(&console_buf_queue, &n); + + osalDbgAssert(buf != NULL, "queue is empty"); + + /* zero the rest of the buffer (buf should point to allocated space) */ + for(i=n; i + * + * Based on the following work: + * - Guillaume Duc's raw hid example (MIT License) + * https://github.com/guiduc/usb-hid-chibios-example + * - PJRC Teensy examples (MIT License) + * https://www.pjrc.com/teensy/usb_keyboard.html + * - hasu's TMK keyboard code (GPL v2 and some code Modified BSD) + * https://github.com/tmk/tmk_keyboard/ + * - ChibiOS demo code (Apache 2.0 License) + * http://www.chibios.org + * + * Since some GPL'd code is used, this work is licensed under + * GPL v2 or later. + */ + + +#ifndef _USB_MAIN_H_ +#define _USB_MAIN_H_ + +// TESTING +// extern uint8_t blinkLed; + +#include "ch.h" +#include "hal.h" + +/* ------------------------- + * General USB driver header + * ------------------------- + */ + +/* The USB driver to use */ +#define USB_DRIVER USBD1 + +/* Initialize the USB driver and bus */ +void init_usb_driver(USBDriver *usbp); + +/* Send remote wakeup packet */ +void send_remote_wakeup(USBDriver *usbp); + +/* --------------- + * Keyboard header + * --------------- + */ + +/* main keyboard (6kro) */ +#define KBD_INTERFACE 0 +#define KBD_ENDPOINT 1 +#define KBD_EPSIZE 8 +#define KBD_REPORT_KEYS (KBD_EPSIZE - 2) + +/* secondary keyboard */ +#ifdef NKRO_ENABLE +#define NKRO_INTERFACE 4 +#define NKRO_ENDPOINT 5 +#define NKRO_EPSIZE 16 +#define NKRO_REPORT_KEYS (NKRO_EPSIZE - 1) +#endif + +/* this defines report_keyboard_t and computes REPORT_SIZE defines */ +// #include "report.h" + +/* extern report_keyboard_t keyboard_report_sent; */ + +/* keyboard IN request callback handler */ +void kbd_in_cb(USBDriver *usbp, usbep_t ep); + +/* start-of-frame handler */ +void kbd_sof_cb(USBDriver *usbp); + +#ifdef NKRO_ENABLE +/* nkro IN callback hander */ +void nkro_in_cb(USBDriver *usbp, usbep_t ep); +#endif /* NKRO_ENABLE */ + +/* ------------ + * Mouse header + * ------------ + */ + +#ifdef MOUSE_ENABLE + +#define MOUSE_INTERFACE 1 +#define MOUSE_ENDPOINT 2 +#define MOUSE_EPSIZE 8 + +/* mouse IN request callback handler */ +void mouse_in_cb(USBDriver *usbp, usbep_t ep); +#endif /* MOUSE_ENABLE */ + +/* --------------- + * Extrakey header + * --------------- + */ + +#ifdef EXTRAKEY_ENABLE + +#define EXTRA_INTERFACE 3 +#define EXTRA_ENDPOINT 4 +#define EXTRA_EPSIZE 8 + +/* extrakey IN request callback handler */ +void extra_in_cb(USBDriver *usbp, usbep_t ep); + +/* extra report structure */ +typedef struct { + uint8_t report_id; + uint16_t usage; +} __attribute__ ((packed)) report_extra_t; +#endif /* EXTRAKEY_ENABLE */ + +/* -------------- + * Console header + * -------------- + */ + +#ifdef CONSOLE_ENABLE + +#define CONSOLE_INTERFACE 2 +#define CONSOLE_ENDPOINT 3 +#define CONSOLE_EPSIZE 16 + +/* Number of IN reports that can be stored inside the output queue */ +#define CONSOLE_QUEUE_CAPACITY 2 +#define CONSOLE_QUEUE_BUFFER_SIZE (CONSOLE_QUEUE_CAPACITY * CONSOLE_EPSIZE) + +/* Console flush time */ +#define CONSOLE_FLUSH_MS 50 + +/* Putchar over the USB console */ +int8_t sendchar(uint8_t c); + +/* Flush output (send everything immediately) */ +void console_flush_output(void); + +/* console IN request callback handler */ +void console_in_cb(USBDriver *usbp, usbep_t ep); +#endif /* CONSOLE_ENABLE */ + +void sendchar_pf(void *p, char c); + +#endif /* _USB_MAIN_H_ */ diff --git a/protocol/serial_soft.c b/protocol/serial_soft.c index 44822b7e..569205bf 100644 --- a/protocol/serial_soft.c +++ b/protocol/serial_soft.c @@ -68,7 +68,6 @@ POSSIBILITY OF SUCH DAMAGE. #endif /* debug for signal timing, see debug pin with oscilloscope */ -#define SERIAL_SOFT_DEBUG #ifdef SERIAL_SOFT_DEBUG #define SERIAL_SOFT_DEBUG_INIT() (DDRD |= 1<<7) #define SERIAL_SOFT_DEBUG_TGL() (PORTD ^= 1<<7) @@ -176,7 +175,7 @@ void serial_send(uint8_t data) ISR(SERIAL_SOFT_RXD_VECT) { SERIAL_SOFT_DEBUG_TGL(); - SERIAL_SOFT_RXD_INT_ENTER() + SERIAL_SOFT_RXD_INT_ENTER(); uint8_t data = 0; diff --git a/protocol/usb_hid/parser.cpp b/protocol/usb_hid/parser.cpp index 1a152ff3..fe002c0d 100644 --- a/protocol/usb_hid/parser.cpp +++ b/protocol/usb_hid/parser.cpp @@ -13,7 +13,7 @@ void KBDReportParser::Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) bool is_error = false; report_keyboard_t *report = (report_keyboard_t *)buf; - dprintf("KBDReport: %02X %02X", report->mods, report->reserved); + dprintf("keyboard input: %02X %02X", report->mods, report->reserved); for (uint8_t i = 0; i < KEYBOARD_REPORT_KEYS; i++) { if (IS_ERROR(report->keys[i])) { is_error = true; diff --git a/rules.mk b/rules.mk index 860fc1a9..0ae84d91 100644 --- a/rules.mk +++ b/rules.mk @@ -425,7 +425,6 @@ ifneq (, $(findstring 0.7, $(shell dfu-programmer --version 2>&1))) else dfu-programmer $(MCU) erase endif - dfu-programmer $(MCU) erase dfu-programmer $(MCU) flash $(TARGET).hex dfu-programmer $(MCU) reset diff --git a/tool/chibios/.gitignore b/tool/chibios/.gitignore new file mode 100644 index 00000000..3118dbef --- /dev/null +++ b/tool/chibios/.gitignore @@ -0,0 +1 @@ +chibios diff --git a/tool/chibios/ch-bootloader-jump.patch b/tool/chibios/ch-bootloader-jump.patch new file mode 100644 index 00000000..d8865762 --- /dev/null +++ b/tool/chibios/ch-bootloader-jump.patch @@ -0,0 +1,116 @@ +diff --git a/os/common/ports/ARMCMx/compilers/GCC/crt0_v6m.s b/os/common/ports/ARMCMx/compilers/GCC/crt0_v6m.s +index 51a79bb..42d07bd 100644 +--- a/os/common/ports/ARMCMx/compilers/GCC/crt0_v6m.s ++++ b/os/common/ports/ARMCMx/compilers/GCC/crt0_v6m.s +@@ -105,6 +105,13 @@ + #define CRT0_CALL_DESTRUCTORS TRUE + #endif + ++/** ++ * @brief Magic number for jumping to bootloader. ++ */ ++#if !defined(MAGIC_BOOTLOADER_NUMBER) || defined(__DOXYGEN__) ++#define MAGIC_BOOTLOADER_NUMBER 0xDEADBEEF ++#endif ++ + /*===========================================================================*/ + /* Code section. */ + /*===========================================================================*/ +@@ -124,6 +131,17 @@ + .thumb_func + .global Reset_Handler + Reset_Handler: ++ ++#ifdef STM32_BOOTLOADER_ADDRESS ++ /* jump to bootloader code */ ++ ldr r0, =__ram0_end__-4 ++ ldr r1, =MAGIC_BOOTLOADER_NUMBER ++ ldr r2, [r0, #0] ++ str r0, [r0, #0] /* erase stored magic */ ++ cmp r2, r1 ++ beq Bootloader_Jump ++#endif /* STM32_BOOTLOADER_ADDRESS */ ++ + /* Interrupts are globally masked initially.*/ + cpsid i + +@@ -242,6 +260,21 @@ endfiniloop: + ldr r1, =__default_exit + bx r1 + ++#ifdef STM32_BOOTLOADER_ADDRESS ++/* ++ * Jump-to-bootloader function. ++ */ ++ ++ .align 2 ++ .thumb_func ++Bootloader_Jump: ++ ldr r0, =STM32_BOOTLOADER_ADDRESS ++ ldr r1, [r0, #0] ++ mov sp, r1 ++ ldr r0, [r0, #4] ++ bx r0 ++#endif /* STM32_BOOTLOADER_ADDRESS */ ++ + #endif + + /** @} */ +diff --git a/os/common/ports/ARMCMx/compilers/GCC/crt0_v7m.s b/os/common/ports/ARMCMx/compilers/GCC/crt0_v7m.s +index 4812a29..dca9f88 100644 +--- a/os/common/ports/ARMCMx/compilers/GCC/crt0_v7m.s ++++ b/os/common/ports/ARMCMx/compilers/GCC/crt0_v7m.s +@@ -140,6 +140,13 @@ + #define CRT0_CPACR_INIT 0x00F00000 + #endif + ++/** ++ * @brief Magic number for jumping to bootloader. ++ */ ++#if !defined(MAGIC_BOOTLOADER_NUMBER) || defined(__DOXYGEN__) ++#define MAGIC_BOOTLOADER_NUMBER 0xDEADBEEF ++#endif ++ + /*===========================================================================*/ + /* Code section. */ + /*===========================================================================*/ +@@ -164,6 +171,17 @@ + .thumb_func + .global Reset_Handler + Reset_Handler: ++ ++#ifdef STM32_BOOTLOADER_ADDRESS ++ /* jump to bootloader code */ ++ ldr r0, =__ram0_end__-4 ++ ldr r1, =MAGIC_BOOTLOADER_NUMBER ++ ldr r2, [r0, #0] ++ str r0, [r0, #0] /* erase stored magic */ ++ cmp r2, r1 ++ beq Bootloader_Jump ++#endif /* STM32_BOOTLOADER_ADDRESS */ ++ + /* Interrupts are globally masked initially.*/ + cpsid i + +@@ -305,6 +323,21 @@ endfiniloop: + /* Branching to the defined exit handler.*/ + b __default_exit + ++#ifdef STM32_BOOTLOADER_ADDRESS ++/* ++ * Jump-to-bootloader function. ++ */ ++ ++ .align 2 ++ .thumb_func ++Bootloader_Jump: ++ ldr r0, =STM32_BOOTLOADER_ADDRESS ++ ldr r1, [r0, #0] ++ mov sp, r1 ++ ldr r0, [r0, #4] ++ bx r0 ++#endif /* STM32_BOOTLOADER_ADDRESS */ ++ + #endif /* !defined(__DOXYGEN__) */ + + /** @} */ diff --git a/tool/chibios/chibios.mk b/tool/chibios/chibios.mk new file mode 100644 index 00000000..a67e067a --- /dev/null +++ b/tool/chibios/chibios.mk @@ -0,0 +1,231 @@ +############################################################################## +# Build global options +# NOTE: Can be overridden externally. +# + +# Compiler options here. +ifeq ($(USE_OPT),) + USE_OPT = -O2 -ggdb -fomit-frame-pointer -falign-functions=16 -std=gnu99 -DPROTOCOL_CHIBIOS +endif + +# C specific options here (added to USE_OPT). +ifeq ($(USE_COPT),) + USE_COPT = +endif + +# include specific config.h? +ifdef CONFIG_H + USE_COPT += -include $(CONFIG_H) +endif + +# C++ specific options here (added to USE_OPT). +ifeq ($(USE_CPPOPT),) + USE_CPPOPT = -fno-rtti +endif + +# Enable this if you want the linker to remove unused code and data +ifeq ($(USE_LINK_GC),) + USE_LINK_GC = yes +endif + +# Linker extra options here. +ifeq ($(USE_LDOPT),) + USE_LDOPT = +endif + +# Enable this if you want link time optimizations (LTO) +ifeq ($(USE_LTO),) + USE_LTO = yes +endif + +# If enabled, this option allows to compile the application in THUMB mode. +ifeq ($(USE_THUMB),) + USE_THUMB = yes +endif + +# Enable this if you want to see the full log while compiling. +ifeq ($(USE_VERBOSE_COMPILE),) + USE_VERBOSE_COMPILE = no +endif + +# If enabled, this option makes the build process faster by not compiling +# modules not used in the current configuration. +ifeq ($(USE_SMART_BUILD),) + USE_SMART_BUILD = yes +endif + +# +# Build global options +############################################################################## + +############################################################################## +# Architecture or project specific options +# + +# Stack size to be allocated to the Cortex-M process stack. This stack is +# the stack used by the main() thread. +ifeq ($(USE_PROCESS_STACKSIZE),) + USE_PROCESS_STACKSIZE = 0x200 +endif + +# Stack size to the allocated to the Cortex-M main/exceptions stack. This +# stack is used for processing interrupts and exceptions. +ifeq ($(USE_EXCEPTIONS_STACKSIZE),) + USE_EXCEPTIONS_STACKSIZE = 0x400 +endif + +# +# Architecture or project specific options +############################################################################## + +############################################################################## +# Project, sources and paths +# + +# Imported source files and paths +CHIBIOS = $(TMK_DIR)/tool/chibios/chibios +# Startup files. +include $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC/mk/startup_$(MCU_STARTUP).mk +# HAL-OSAL files (optional). +include $(CHIBIOS)/os/hal/hal.mk +include $(CHIBIOS)/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)/platform.mk +ifneq ("$(wildcard $(TARGET_DIR)/boards/$(BOARD))","") + include $(TARGET_DIR)/boards/$(BOARD)/board.mk +else + include $(CHIBIOS)/os/hal/boards/$(BOARD)/board.mk +endif +include $(CHIBIOS)/os/hal/osal/rt/osal.mk +# RTOS files (optional). +include $(CHIBIOS)/os/rt/rt.mk +include $(CHIBIOS)/os/rt/ports/ARMCMx/compilers/GCC/mk/port_v$(ARMV)m.mk +# Other files (optional). + +# Define linker script file here +ifneq ("$(wildcard $(TARGET_DIR)/ld/$(MCU_LDSCRIPT).ld)","") +LDSCRIPT = $(TARGET_DIR)/ld/$(MCU_LDSCRIPT).ld +else +LDSCRIPT = $(STARTUPLD)/$(MCU_LDSCRIPT).ld +endif + +# C sources that can be compiled in ARM or THUMB mode depending on the global +# setting. +CSRC = $(STARTUPSRC) \ + $(KERNSRC) \ + $(PORTSRC) \ + $(OSALSRC) \ + $(HALSRC) \ + $(PLATFORMSRC) \ + $(BOARDSRC) \ + $(CHIBIOS)/os/hal/lib/streams/chprintf.c \ + $(TMK_DIR)/protocol/chibios/usb_main.c \ + $(TMK_DIR)/protocol/chibios/main.c \ + $(SRC) + +# C++ sources that can be compiled in ARM or THUMB mode depending on the global +# setting. +CPPSRC = + +# C sources to be compiled in ARM mode regardless of the global setting. +# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler +# option that results in lower performance and larger code size. +ACSRC = + +# C++ sources to be compiled in ARM mode regardless of the global setting. +# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler +# option that results in lower performance and larger code size. +ACPPSRC = + +# C sources to be compiled in THUMB mode regardless of the global setting. +# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler +# option that results in lower performance and larger code size. +TCSRC = + +# C sources to be compiled in THUMB mode regardless of the global setting. +# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler +# option that results in lower performance and larger code size. +TCPPSRC = + +# List ASM source files here +ASMSRC = $(STARTUPASM) $(PORTASM) $(OSALASM) + +INCDIR = $(STARTUPINC) $(KERNINC) $(PORTINC) $(OSALINC) \ + $(HALINC) $(PLATFORMINC) $(BOARDINC) $(TESTINC) \ + $(CHIBIOS)/os/hal/lib/streams $(CHIBIOS)/os/various \ + $(TMK_DIR) $(COMMON_DIR) $(TMK_DIR)/protocol/chibios \ + $(TMK_DIR)/protocol $(TARGET_DIR) + +# +# Project, sources and paths +############################################################################## + +############################################################################## +# Compiler settings +# + +#TRGT = arm-elf- +TRGT = arm-none-eabi- +CC = $(TRGT)gcc +CPPC = $(TRGT)g++ +# Enable loading with g++ only if you need C++ runtime support. +# NOTE: You can use C++ even without C++ support if you are careful. C++ +# runtime support makes code size explode. +LD = $(TRGT)gcc +#LD = $(TRGT)g++ +CP = $(TRGT)objcopy +AS = $(TRGT)gcc -x assembler-with-cpp +AR = $(TRGT)ar +OD = $(TRGT)objdump +SZ = $(TRGT)size +HEX = $(CP) -O ihex +BIN = $(CP) -O binary + +# ARM-specific options here +AOPT = + +# THUMB-specific options here +TOPT = -mthumb -DTHUMB + +# Define C warning options here +CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes -Wno-missing-field-initializers + +# Define C++ warning options here +CPPWARN = -Wall -Wextra -Wundef + +# +# Compiler settings +############################################################################## + +############################################################################## +# Start of user section +# + +# List all user C define here, like -D_DEBUG=1 +## Select which interfaces to include here! +UDEFS = $(OPT_DEFS) + +# Define ASM defines here +UADEFS = $(OPT_DEFS) +# bootloader definitions may be used in the startup .s file +ifneq ("$(wildcard $(TARGET_DIR)/bootloader_defs.h)","") + UADEFS += -include $(TARGET_DIR)/bootloader_defs.h + UDEFS += -include $(TARGET_DIR)/bootloader_defs.h +else ifneq ("$(wildcard $(TARGET_DIR)/boards/$(BOARD)/bootloader_defs.h)","") + UADEFS += -include $(TARGET_DIR)/boards/$(BOARD)/bootloader_defs.h + UDEFS += -include $(TARGET_DIR)/boards/$(BOARD)/bootloader_defs.h +endif + +# List all user directories here +UINCDIR = + +# List the user directory to look for the libraries here +ULIBDIR = + +# List all user libraries here +ULIBS = + +# +# End of user defines +############################################################################## + +RULESPATH = $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC +include $(RULESPATH)/rules.mk diff --git a/tool/chibios/common.mk b/tool/chibios/common.mk new file mode 100644 index 00000000..7e3cccc9 --- /dev/null +++ b/tool/chibios/common.mk @@ -0,0 +1,85 @@ +COMMON_DIR = $(TMK_DIR)/common +SRC += $(COMMON_DIR)/host.c \ + $(COMMON_DIR)/keyboard.c \ + $(COMMON_DIR)/action.c \ + $(COMMON_DIR)/action_tapping.c \ + $(COMMON_DIR)/action_macro.c \ + $(COMMON_DIR)/action_layer.c \ + $(COMMON_DIR)/action_util.c \ + $(COMMON_DIR)/keymap.c \ + $(COMMON_DIR)/print.c \ + $(COMMON_DIR)/debug.c \ + $(COMMON_DIR)/util.c \ + $(COMMON_DIR)/chibios/suspend.c \ + $(COMMON_DIR)/chibios/printf.c \ + $(COMMON_DIR)/chibios/timer.c \ + $(COMMON_DIR)/chibios/bootloader.c + + +# Option modules +ifdef BOOTMAGIC_ENABLE + SRC += $(COMMON_DIR)/bootmagic.c + SRC += $(COMMON_DIR)/chibios/eeconfig.c + OPT_DEFS += -DBOOTMAGIC_ENABLE +endif + +ifdef MOUSEKEY_ENABLE + SRC += $(COMMON_DIR)/mousekey.c + OPT_DEFS += -DMOUSEKEY_ENABLE + OPT_DEFS += -DMOUSE_ENABLE +endif + +ifdef EXTRAKEY_ENABLE + OPT_DEFS += -DEXTRAKEY_ENABLE +endif + +ifdef CONSOLE_ENABLE + OPT_DEFS += -DCONSOLE_ENABLE +else + OPT_DEFS += -DNO_PRINT + OPT_DEFS += -DNO_DEBUG +endif + +ifdef COMMAND_ENABLE + SRC += $(COMMON_DIR)/command.c + OPT_DEFS += -DCOMMAND_ENABLE +endif + +ifdef NKRO_ENABLE + OPT_DEFS += -DNKRO_ENABLE +endif + +ifdef USB_6KRO_ENABLE + OPT_DEFS += -DUSB_6KRO_ENABLE +endif + +ifdef SLEEP_LED_ENABLE + SRC += $(COMMON_DIR)/chibios/sleep_led.c + OPT_DEFS += -DSLEEP_LED_ENABLE + OPT_DEFS += -DNO_SUSPEND_POWER_DOWN +endif + +ifdef BACKLIGHT_ENABLE + SRC += $(COMMON_DIR)/backlight.c + OPT_DEFS += -DBACKLIGHT_ENABLE +endif + +ifdef KEYMAP_SECTION_ENABLE + OPT_DEFS += -DKEYMAP_SECTION_ENABLE + + ifeq ($(strip $(MCU)),atmega32u2) + EXTRALDFLAGS = -Wl,-L$(TMK_DIR),-Tldscript_keymap_avr35.x + else ifeq ($(strip $(MCU)),atmega32u4) + EXTRALDFLAGS = -Wl,-L$(TMK_DIR),-Tldscript_keymap_avr5.x + else + EXTRALDFLAGS = $(error no ldscript for keymap section) + endif +endif + +# Version string +OPT_DEFS += -DVERSION=$(shell (git describe --always --dirty || echo 'unknown') 2> /dev/null) + +# Bootloader address +ifdef BOOTLOADER_ADDRESS + OPT_DEFS += -DBOOTLOADER_ADDRESS=$(BOOTLOADER_ADDRESS) +endif diff --git a/tool/mbed/common.mk b/tool/mbed/common.mk index 77bf7c3e..a0aa8717 100644 --- a/tool/mbed/common.mk +++ b/tool/mbed/common.mk @@ -63,7 +63,7 @@ endif ifdef SLEEP_LED_ENABLE $(error Not Supported) - SRC += common/sleep_led.c + SRC += common/mbed/sleep_led.c OPT_DEFS += -DSLEEP_LED_ENABLE OPT_DEFS += -DNO_SUSPEND_POWER_DOWN endif