diff --git a/device/src/keyboard/charger.c b/device/src/keyboard/charger.c index 609e350d..498beeac 100644 --- a/device/src/keyboard/charger.c +++ b/device/src/keyboard/charger.c @@ -4,6 +4,7 @@ #include "charger.h" #include "keyboard/charger.h" #include "nrf52840.h" +#include "power_mode.h" #include "shell.h" #include "timer.h" #include "event_scheduler.h" @@ -132,27 +133,6 @@ void Charger_EnableCharging(bool enabled) { } } -static bool isStillDepleted() { - updatePowered(); - return !batteryState.powered; -} - -void Charger_EnterSleepIfDepleted(bool enter) { - if (enter) { - NVIC_SystemReset(); - } - if (isStillDepleted()) { - LogU("Going to enter low power mode because of depleted battery!\n"); - while (isStillDepleted()) { - - k_sleep(K_MSEC(10000)); - } - - NVIC_SystemReset(); - } -} - - void updateChargerEnabled(battery_state_t *batteryState, battery_manager_config_t* config) { battery_manager_automaton_state_t newState = BatteryManager_UpdateState( currentChargingAutomatonState, @@ -164,7 +144,7 @@ void updateChargerEnabled(battery_state_t *batteryState, battery_manager_config_ currentChargingAutomatonState = newState; switch (newState) { case BatteryManagerAutomatonState_TurnOff: - Charger_EnterSleepIfDepleted(true); + PowerMode_ActivateMode(PowerMode_ShutDown, false); break; case BatteryManagerAutomatonState_Charging: Charger_EnableCharging(true); @@ -305,11 +285,30 @@ void chargerStatCallback(const struct device *port, struct gpio_callback *cb, gp EventScheduler_Reschedule(CurrentTime + CHARGER_STAT_PERIOD, EventSchedulerEvent_UpdateBattery, "charger - stat callback"); } -// charging battery with CHARGER_EN yields STAT 0 -// fully charged battery with CHARGER_EN yields STAT 1 -// CHARGER_EN 0 yields STAT 1 +bool Charger_ShouldRemainInDepletedMode(bool checkVoltage) { + updatePowered(); + if (checkVoltage) { + uint16_t voltage = getVoltage(); + return !batteryState.powered && voltage < getCurrentBatteryConfig()->minWakeupVoltage; + } else { + return !batteryState.powered; + } +} + +bool Charger_ShouldEnterDepletedMode() { + updatePowered(); + uint16_t voltage = getVoltage(); + return !batteryState.powered && voltage < getCurrentBatteryConfig()->minVoltage; +} + +void InitCharger_Min(void) { + adc_channel_setup_dt(&adc_channel); + (void)adc_sequence_init_dt(&adc_channel, &sequence); +} void InitCharger(void) { + InitCharger_Min(); + gpio_pin_configure_dt(&chargerEnDt, GPIO_OUTPUT); Charger_EnableCharging(true); @@ -318,10 +317,6 @@ void InitCharger(void) { gpio_init_callback(&callbackStruct, chargerStatCallback, BIT(chargerStatDt.pin)); gpio_add_callback(chargerStatDt.port, &callbackStruct); - adc_channel_setup_dt(&adc_channel); - - (void)adc_sequence_init_dt(&adc_channel, &sequence); - const nrfx_power_usbevt_config_t config = { .handler = &powerCallback }; @@ -334,3 +329,4 @@ void InitCharger(void) { // TODO: Update battery level. See bas_notify() } + diff --git a/device/src/keyboard/charger.h b/device/src/keyboard/charger.h index c5528af3..6bf65c40 100644 --- a/device/src/keyboard/charger.h +++ b/device/src/keyboard/charger.h @@ -36,9 +36,12 @@ // Functions: void InitCharger(void); + void InitCharger_Min(void); void Charger_PrintState(); void Charger_UpdateBatteryState(); void Charger_EnableCharging(bool enabled); - void Charger_EnterSleepIfDepleted(bool enter); + + bool Charger_ShouldRemainInDepletedMode(bool checkVoltage); + bool Charger_ShouldEnterDepletedMode(); #endif // __CHARGER_H__ diff --git a/device/src/keyboard/key_scanner.c b/device/src/keyboard/key_scanner.c index 2339999e..f57a1790 100644 --- a/device/src/keyboard/key_scanner.c +++ b/device/src/keyboard/key_scanner.c @@ -23,6 +23,8 @@ #include "layouts/key_layout.h" #include "layouts/key_layout_80_to_universal.h" #include "test_switches.h" +#include "power_mode.h" +#include "keyboard/leds.h" // Thread definitions @@ -63,6 +65,8 @@ volatile bool KeyPressed; volatile bool KeyScanner_ResendKeyStates = false; +static void scanAllKeys(); + ATTR_UNUSED static void reportChange(uint8_t sourceIndex, bool active) { uint8_t slotId = DEVICE_IS_UHK80_LEFT ? SlotId_LeftKeyboardHalf : SlotId_RightKeyboardHalf; uint8_t keyId = KeyLayout_Uhk80_to_Uhk60[slotId][sourceIndex]; @@ -74,12 +78,106 @@ ATTR_UNUSED static void reportChange(uint8_t sourceIndex, bool active) { } } -static void scanKeys() { +static bool scanKey(uint8_t rowId, uint8_t colId) { + gpio_pin_set_dt(&rows[rowId], 1); + bool keyState = gpio_pin_get_dt(&cols[colId]); + gpio_pin_set_dt(&rows[rowId], 0); + return keyState; +} + +static bool isSfjlKey(uint8_t rowId, uint8_t colId) { + if (DEVICE_IS_UHK80_LEFT) { + return (rowId == 3 && (colId == 2 || colId == 4)); + } + if (DEVICE_IS_UHK80_RIGHT) { + return (rowId == 3 && (colId == 1 || colId == 3)); + } + return false; +} + +static sfjl_scan_result_t scanSfjl(bool fullScan) { + bool somethingPressed = false; + bool success = true; + +#define CHECK(EXPECTED, EXPR) if (EXPR) { success &= EXPECTED; somethingPressed = true; } else { success &= !EXPECTED; } + + if (DEVICE_IS_UHK80_LEFT) { + CHECK(true, scanKey(3, 2)); + CHECK(true, scanKey(3, 4)); + } + + if (DEVICE_IS_UHK80_RIGHT) { + CHECK(true, scanKey(3, 1)); + CHECK(true, scanKey(3, 3)); + } + + for (uint8_t rowId=0; rowId PowerMode_LightSleep) { + defaultResult = MAX(defaultResult, scanSfjl(fullScan)); + } + return defaultResult; +} + +static bool scanSfjlWithBlinking(bool fullScan) { + const uint16_t blinkCount = 3; + const uint16_t blinktimeOn = 100; + const uint16_t blinktimeOff = 200; + const uint16_t scanInterval = PowerModeConfig[CurrentPowerMode].keyScanInterval; + + sfjl_scan_result_t result = SfjlScanResult_NonePressed; + + result = scanKeysOnce(result, fullScan); + + if (result == SfjlScanResult_NonePressed || !fullScan) { + return result == SfjlScanResult_FullMatch; + } + + for (uint16_t i=0; i PowerMode_LightSleep) { + KeyScanner_ScanAndWakeOnSfjl(true, true); + } else { + scanAllKeys(); + } +} + void keyScanner() { while (true) { scanKeys(); - k_msleep(1); + k_msleep(PowerModeConfig[CurrentPowerMode].keyScanInterval); } } -void InitKeyScanner(void) -{ +void InitKeyScanner_Min(void) { for (uint8_t rowId=0; rowId<6; rowId++) { gpio_pin_configure_dt(&rows[rowId], GPIO_OUTPUT); } for (uint8_t colId=0; colIdisIso != buffer->isIso) { HardwareConfig->isIso = buffer->isIso; Ledmap_InitLedLayout(); + LedManager_RecalculateLedBrightness(); Ledmap_UpdateBacklightLeds(); } } @@ -480,6 +481,9 @@ static void receiveProperty(device_id_t src, state_sync_prop_id_t propId, const EventScheduler_Schedule(CurrentTime + 1000, EventSchedulerEvent_UpdateBattery, "state sync"); break; case StateSyncPropertyId_PowerMode: + if (!isLocalUpdate) { + PowerMode_ActivateMode(*(power_mode_t *)data, false); + } break; default: printk("Property %i ('%s') has no receive handler. If this is correct, please add a " @@ -630,6 +634,10 @@ static void prepareData(device_id_t dst, const uint8_t *propDataPtr, state_sync_ submitPreparedData(dst, propId, (const uint8_t *)&dongleProtocolVersion, sizeof(dongleProtocolVersion)); return; } + case StateSyncPropertyId_PowerMode: { + submitPreparedData(dst, propId, (const uint8_t *)&CurrentPowerMode, sizeof(CurrentPowerMode)); + return; + } case StateSyncPropertyId_KeyStatesDummy: { #if DEVICE_IS_KEYBOARD KeyScanner_ResendKeyStates = true; @@ -691,7 +699,7 @@ static update_result_t handlePropertyUpdateRightToLeft() { if (KeyBacklightBrightness != 0) { // Update relevant data - UPDATE_AND_RETURN_IF_DIRTY(StateSyncPropertyId_PowerMode, UpdateResult_UpdatedHighPrio); + UPDATE_AND_RETURN_IF_DIRTY(StateSyncPropertyId_PowerMode, UpdateResult_UpdatedHighPrio); //has to be before Backlight UPDATE_AND_RETURN_IF_DIRTY(StateSyncPropertyId_FunctionalColors, UpdateResult_UpdatedHighPrio); UPDATE_AND_RETURN_IF_DIRTY(StateSyncPropertyId_LayerActionFirst + ActiveLayer, UpdateResult_UpdatedHighPrio); UPDATE_AND_RETURN_IF_DIRTY(StateSyncPropertyId_ActiveKeymap, UpdateResult_UpdatedHighPrio); @@ -705,6 +713,7 @@ static update_result_t handlePropertyUpdateRightToLeft() { UPDATE_AND_RETURN_IF_DIRTY(propId, UpdateResult_UpdatedLowPrio); } } else { + UPDATE_AND_RETURN_IF_DIRTY(StateSyncPropertyId_PowerMode, UpdateResult_UpdatedHighPrio); UPDATE_AND_RETURN_IF_DIRTY(StateSyncPropertyId_Backlight, UpdateResult_UpdatedHighPrio); } diff --git a/doc-dev/reference-manual.md b/doc-dev/reference-manual.md index 5aa56db9..fca96978 100644 --- a/doc-dev/reference-manual.md +++ b/doc-dev/reference-manual.md @@ -114,7 +114,7 @@ COMMAND = {startMouse|stopMouse} {move DIRECTION|scroll DIRECTION|accelerate|dec COMMAND = setVar COMMAND = {pressKey|holdKey|tapKey|releaseKey} SHORTCUT COMMAND = tapKeySeq [SHORTCUT]+ -COMMAND = powerMode [toggle] { wake | lightSleep | sleep | deepSleep } +COMMAND = powerMode [toggle] { wake | lock | sleep | shutDown } COMMAND = switchHost { last | next | previous | | } COMMAND = set module.MODULEID.navigationMode.LAYERID_BASIC NAVIGATION_MODE COMMAND = set module.MODULEID.baseSpeed @@ -324,11 +324,11 @@ COMMAND = setEmergencyKey KEYID - `resetTrackpoint` resets the internal trackpoint board. Can be used to recover the trackpoint from drift conditions. Drifts usually happen if you keep the cursor moving at slow constant speeds, because of the boards's internal adaptive calibration. Since the board's parameters cannot be altered, the only way around is or you to learn not to do the type of movement which triggers them. - `i2cBaudRate ` sets i2c baud rate. Lowering this value may improve module reliability, while increasing latency. - `{|}` Braces allow grouping multiple commands as if they were a single command. Please note that from the point of view of the engine, braces are (almost) regular commands, and have to be followed by newlines like any other command. Therefore idioms like `} else {` are not possible at the moment. -- `powerMode [toggle] { wake | lightSleep | sleep | deepSleep }` - - `lightSleep` disables all leds. When any key is pressed, the uhk is waked up, and remote wakeup of the host is attempted. - - `deepSleep` disables all leds, disables USB output, and (in the future will) put the device into a low-power mode. - - `sleep` is a general alias that at the moment points to `deepSleep`. - - `wake` wakes up the device from "any" sleep mode (that doesn't disable macro engine and the half link). +- `powerMode [toggle] { wake | lock | sleep | shutDown }` + - `lock` disables all leds, disables USB output. Connections remain active. Device can be woken up by either pressing s+f and j+l keys, or by another macro call. This mode is experimental. + - `sleep` reboots the keyboard into a low power mode, that still scans keys and can be woken up by s+f or j+l keys. + - `shutDown` is used by uhk when its battery runs out. You can wake up by plugging in the USB cable. + - `wake` wakes up the device from "any" sleep mode that doesn't disable macro engine and the half link. Further rules: - If a sleep mode is activated while another sleep mode is active, the deeper of them will be activated. - If `toggle` is specified and the device is already in the (exact) sleep mode, it will wake the device instead. diff --git a/right/src/CMakeLists.txt b/right/src/CMakeLists.txt index 785cb8d6..6de0c36c 100644 --- a/right/src/CMakeLists.txt +++ b/right/src/CMakeLists.txt @@ -47,4 +47,5 @@ target_sources(${PROJECT_NAME} PRIVATE usb_report_updater.c user_logic.c utils.c + wormhole.c ) diff --git a/right/src/event_scheduler.c b/right/src/event_scheduler.c index dfc1e023..eef0cd34 100644 --- a/right/src/event_scheduler.c +++ b/right/src/event_scheduler.c @@ -106,6 +106,9 @@ static void processEvt(event_scheduler_event_t evt) case EventSchedulerEvent_PowerMode: PowerMode_Update(); break; + case EventSchedulerEvent_PowerModeRestart: + PowerMode_Restart(); + break; case EventSchedulerEvent_EndBtPairing: BtPair_EndPairing(false, "Pairing timeout"); break; diff --git a/right/src/event_scheduler.h b/right/src/event_scheduler.h index 7eb6dd7b..dd870b4b 100644 --- a/right/src/event_scheduler.h +++ b/right/src/event_scheduler.h @@ -33,6 +33,7 @@ EventSchedulerEvent_ReenableUart, EventSchedulerEvent_UpdateMergeSensor, EventSchedulerEvent_PowerMode, + EventSchedulerEvent_PowerModeRestart, EventSchedulerEvent_EndBtPairing, EventSchedulerEvent_RestartBt, EventSchedulerEvent_BtStartScanningAndAdvertising, diff --git a/right/src/led_manager.c b/right/src/led_manager.c index 46f409bc..fba59d01 100644 --- a/right/src/led_manager.c +++ b/right/src/led_manager.c @@ -32,7 +32,7 @@ uint8_t KeyBacklightBrightness = 0xff; static void recalculateLedBrightness() { - bool globalSleepMode = !Cfg.LedsEnabled || CurrentPowerMode > PowerMode_Awake || Cfg.LedBrightnessMultiplier == 0.0f; + bool globalSleepMode = !Cfg.LedsEnabled || CurrentPowerMode > PowerMode_LastAwake || Cfg.LedBrightnessMultiplier == 0.0f; bool globalAlwaysOn = Cfg.LedsAlwaysOn || Ledmap_AlwaysOn || InteractivePairingInProgress; if (!globalAlwaysOn && (globalSleepMode || KeyBacklightSleepModeActive)) { diff --git a/right/src/ledmap.c b/right/src/ledmap.c index 0eacadd5..819eb676 100644 --- a/right/src/ledmap.c +++ b/right/src/ledmap.c @@ -618,6 +618,18 @@ backlighting_mode_t Ledmap_GetEffectiveBacklightMode() { } } +static void setKeyColor(const rgb_t* color, uint8_t slotId, uint8_t keyId) { + setPerKeyColor(color, determineMode(slotId), slotId, keyId); +} + +void Ledmap_SetSfjlValues(void) { + setEntireMatrix(0); + setKeyColor(&white, SlotId_LeftKeyboardHalf, 15); + setKeyColor(&white, SlotId_LeftKeyboardHalf, 17); + setKeyColor(&white, SlotId_RightKeyboardHalf, 16); + setKeyColor(&white, SlotId_RightKeyboardHalf, 18); +} + void handleModeChange(backlighting_mode_t from, backlighting_mode_t to) { if (to == BacklightingMode_LightAll) { setEntireMatrix(255); diff --git a/right/src/ledmap.h b/right/src/ledmap.h index 3797096a..00e479dd 100644 --- a/right/src/ledmap.h +++ b/right/src/ledmap.h @@ -49,6 +49,7 @@ void Ledmap_UpdateBacklightLeds(void); void Ledmap_InitLedLayout(void); + void Ledmap_SetSfjlValues(void); void Ledmap_ActivateTestled(uint8_t slotId, uint8_t keyId); void Ledmap_ActivateTestLedMode(bool active); void Ledmap_SetLedBacklightingMode(backlighting_mode_t newMode); diff --git a/right/src/macros/commands.c b/right/src/macros/commands.c index df1b8e79..a9801ad7 100644 --- a/right/src/macros/commands.c +++ b/right/src/macros/commands.c @@ -1481,28 +1481,30 @@ static macro_result_t processPowerModeCommand(parser_context_t* ctx) { toggle = true; } - if (ConsumeToken(ctx, "deepSleep") || ConsumeToken(ctx, "sleep")) { - if (Macros_DryRun) { - return MacroResult_Finished; - } - PowerMode_ActivateMode(PowerMode_DeepSleep, toggle); - } - else if (ConsumeToken(ctx, "lightSleep")) { - if (Macros_DryRun) { - return MacroResult_Finished; - } - PowerMode_ActivateMode(PowerMode_LightSleep, toggle); + power_mode_t mode = PowerMode_Awake; + + parser_context_t ctxCopy = *ctx; + + if (false) { } + else if (ConsumeToken(ctx, "wake")) { mode = PowerMode_Awake; } + else if (ConsumeToken(ctx, "lock")) { mode = PowerMode_Lock; } + else if (ConsumeToken(ctx, "sleep")) { mode = PowerMode_SfjlSleep; } + else if (ConsumeToken(ctx, "shutDown")) { mode = PowerMode_ShutDown; } + else { + Macros_ReportError("This mode is not available in this release:", ctxCopy.at, ctxCopy.at); } - else if (ConsumeToken(ctx, "wake")) { - if (Macros_DryRun) { - return MacroResult_Finished; - } - PowerMode_ActivateMode(PowerMode_Awake, toggle); + + if (Macros_DryRun || Macros_ParserError) { + return MacroResult_Finished; } - else { - Macros_ReportError("Unrecognized parameter:", ctx->at, ctx->at); + + /* wait until the key is released to prevent backlight flashing */ + if (Macros_CurrentMacroKeyIsActive()) { + return Macros_SleepTillKeystateChange(); } + PowerMode_ActivateMode(mode, toggle); + return MacroResult_Finished; } diff --git a/right/src/main.c b/right/src/main.c index 9a3d4eef..aa137f16 100644 --- a/right/src/main.c +++ b/right/src/main.c @@ -114,7 +114,7 @@ void CopyRightKeystateMatrix(void) } bool UsbReadyForTransfers(void) { - if (UsbReportUpdateSemaphore && CurrentPowerMode != PowerMode_Awake) { + if (UsbReportUpdateSemaphore && CurrentPowerMode > PowerMode_LastAwake) { if (Timer_GetElapsedTime(&UpdateUsbReports_LastUpdateTime) < USB_SEMAPHORE_TIMEOUT) { return false; } else { diff --git a/right/src/power_mode.c b/right/src/power_mode.c index 1beb5066..bb0eea73 100644 --- a/right/src/power_mode.c +++ b/right/src/power_mode.c @@ -1,7 +1,12 @@ #include "power_mode.h" +#include "keyboard/key_scanner.h" +#include "state_sync.h" #include "usb_composite_device.h" #include "event_scheduler.h" #include "led_manager.h" +#include "keyboard/charger.h" +#include "keyboard/leds.h" +#include "wormhole.h" #ifdef __ZEPHYR__ #include "device_state.h" @@ -12,6 +17,39 @@ #include "usb_composite_device.h" #endif +power_mode_config_t PowerModeConfig[PowerMode_Count] = { + [PowerMode_Awake] = { + .name = "Awake", + .i2cInterval = 1, + .keyScanInterval = 1, + }, + [PowerMode_Powersaving] = { + .name = "Awake", + .i2cInterval = 1, + .keyScanInterval = 5, + }, + [PowerMode_LightSleep] = { + .name = "LightSleep", + .i2cInterval = 1, + .keyScanInterval = 10, + }, + [PowerMode_Lock] = { + .name = "Lock", + .i2cInterval = 50, + .keyScanInterval = 50, + }, + [PowerMode_SfjlSleep] = { + .name = "SfjlSleep", + .i2cInterval = 100, + .keyScanInterval = 100, + }, + [PowerMode_ShutDown] = { + .name = "ShutDown", + .i2cInterval = 100, + .keyScanInterval = 100, + }, +}; + ATTR_UNUSED static bool usbAwake = false; power_mode_t CurrentPowerMode = PowerMode_Awake; @@ -27,40 +65,63 @@ void PowerMode_SetUsbAwake(bool awake) { #endif } -void PowerMode_Update() { +static bool isSomeoneAwake() { + connection_target_t ourMaster = DEVICE_IS_UHK80_LEFT ? ConnectionTarget_Right : ConnectionTarget_Host; #ifdef __ZEPHYR__ - bool someoneAwake = DeviceState_IsTargetConnected(ConnectionTarget_Host); + bool someoneAwake = DeviceState_IsTargetConnected(ourMaster); #else - bool someoneAwake = CurrentPowerMode == PowerMode_Awake; + bool someoneAwake = DeviceState_IsTargetConnected(ourMaster); #endif + return someoneAwake; +} - bool newPowerMode = someoneAwake ? PowerMode_Awake : PowerMode_LightSleep; +void PowerMode_Update() { + bool someoneAwake = isSomeoneAwake(); + + power_mode_t newPowerMode = someoneAwake ? PowerMode_Awake : PowerMode_LightSleep; + + if (newPowerMode == someoneAwake) { + newPowerMode = RunningOnBattery ? PowerMode_Powersaving : PowerMode_Awake; + } if (CurrentPowerMode <= PowerMode_LightSleep) { PowerMode_ActivateMode(newPowerMode, false); } } -static void lightSleep() { - CurrentPowerMode = PowerMode_LightSleep; +static void notifyEveryone() { +#ifdef __ZEPHYR__ + StateSync_UpdateProperty(StateSyncPropertyId_PowerMode, NULL); +#endif EventVector_Set(EventVector_LedManagerFullUpdateNeeded); EventVector_WakeMain(); } -static void deepSleep() { - CurrentPowerMode = PowerMode_DeepSleep; +static void lightSleep() { + CurrentPowerMode = PowerMode_LightSleep; + LedManager_RecalculateLedBrightness(); + notifyEveryone(); +} + +static void lock() { + CurrentPowerMode = PowerMode_Lock; + notifyEveryone(); +} - EventVector_Set(EventVector_LedManagerFullUpdateNeeded); - EventVector_WakeMain(); +static void sfjlSleep() { + CurrentPowerMode = PowerMode_SfjlSleep; + notifyEveryone(); } +static void shutDown() { + CurrentPowerMode = PowerMode_ShutDown; + notifyEveryone(); +} static void wake() { CurrentPowerMode = PowerMode_Awake; - - EventVector_Set(EventVector_LedManagerFullUpdateNeeded); - EventVector_WakeMain(); + notifyEveryone(); } void PowerMode_ActivateMode(power_mode_t mode, bool toggle) { @@ -70,7 +131,7 @@ void PowerMode_ActivateMode(power_mode_t mode, bool toggle) { } // if two modes are activated, always sink into the deeper of them - if (mode > PowerMode_Awake && CurrentPowerMode > mode) { + if (mode > PowerMode_LastAwake && CurrentPowerMode > mode) { return; } @@ -86,12 +147,24 @@ void PowerMode_ActivateMode(power_mode_t mode, bool toggle) { case PowerMode_LightSleep: lightSleep(); break; - case PowerMode_DeepSleep: - deepSleep(); + case PowerMode_Lock: + lock(); + break; + case PowerMode_SfjlSleep: + sfjlSleep(); + break; + case PowerMode_ShutDown: + shutDown(); break; default: break; } + + LogU("Entered %s power mode\n", PowerModeConfig[CurrentPowerMode].name); + + if (CurrentPowerMode > PowerMode_Lock) { + EventScheduler_Schedule(CurrentTime + POWER_MODE_RESTART_DELAY, EventSchedulerEvent_PowerModeRestart, "restart power mode"); + } } void PowerMode_WakeHost() { @@ -101,3 +174,71 @@ void PowerMode_WakeHost() { WakeUpHost(); #endif } + +void PowerMode_Restart() { +#if DEVICE_IS_KEYBOARD && defined(__ZEPHYR__) + Wormhole.restartPowerMode = CurrentPowerMode; + NVIC_SystemReset(); +#endif +} + +#if DEVICE_IS_KEYBOARD + +static void runDepletedSleep(bool allowWake) { + // if the keyboard is powered, wait until it is disconnected + while (!Charger_ShouldRemainInDepletedMode(false)) { + if (allowWake && KeyScanner_ScanAndWakeOnSfjl(true, false)) { + return; + } + k_sleep(K_MSEC(PowerModeConfig[CurrentPowerMode].keyScanInterval)); + } + + LogU("Battery is empty. Entering low power mode.\n"); + + while (Charger_ShouldRemainInDepletedMode(allowWake)) { + k_sleep(K_MSEC(5000)); + } + LogU("Waking from low power mode."); +} + +static void runSfjlSleep() { + while (true) { + if (KeyScanner_ScanAndWakeOnSfjl(true, false)) { + return; + } + + if (Charger_ShouldEnterDepletedMode()) { + runDepletedSleep(true); + } + + k_sleep(K_MSEC(PowerModeConfig[CurrentPowerMode].keyScanInterval)); + } +} + +void PowerMode_RestartedTo(power_mode_t mode) { + CurrentPowerMode = mode; + KeyBacklightBrightness = 0; + + InitLeds_Min(); + InitKeyScanner_Min(); + InitCharger_Min(); + + switch (mode) { + case PowerMode_SfjlSleep: + runSfjlSleep(); + break; + case PowerMode_ShutDown: + runDepletedSleep(false); + break; + default: + break; + } + + if (DEVICE_IS_UHK80_LEFT) { + PowerMode_ActivateMode(PowerMode_LightSleep, false); + } else { + PowerMode_ActivateMode(PowerMode_Awake, false); + } +} + +#endif diff --git a/right/src/power_mode.h b/right/src/power_mode.h index 7d6bf9a9..1bf4c784 100644 --- a/right/src/power_mode.h +++ b/right/src/power_mode.h @@ -4,24 +4,37 @@ // Includes: #include + #include // Macros: #define POWER_MODE_UPDATE_DELAY 500 + #define POWER_MODE_RESTART_DELAY 2500 // Typedefs: -typedef enum { - PowerMode_Awake, - PowerMode_LightSleep, - PowerMode_Uhk60Sleep = PowerMode_LightSleep, - PowerMode_DeepSleep, - // PowerMode_ShutDown, currently there is a partial implementation in charger.c, but not connected here. - PowerMode_Count, -} power_mode_t; + typedef struct { + const char* name; + uint16_t i2cInterval; + uint16_t keyScanInterval; + } power_mode_config_t; + + typedef enum { + PowerMode_Awake, + PowerMode_Powersaving, + PowerMode_LastAwake = PowerMode_Powersaving, + PowerMode_LightSleep, + PowerMode_Uhk60Sleep = PowerMode_LightSleep, + PowerMode_Lock, + PowerMode_SfjlSleep, + PowerMode_ShutDown, + PowerMode_Count, + } power_mode_t; + // Variables: extern power_mode_t CurrentPowerMode; + extern power_mode_config_t PowerModeConfig[PowerMode_Count]; // Functions: @@ -30,4 +43,7 @@ typedef enum { void PowerMode_ActivateMode(power_mode_t mode, bool toggle); void PowerMode_WakeHost(); + void PowerMode_RestartedTo(power_mode_t mode); + void PowerMode_Restart(); + #endif diff --git a/right/src/usb_composite_device.c b/right/src/usb_composite_device.c index 4cc4afb0..87e3fb67 100644 --- a/right/src/usb_composite_device.c +++ b/right/src/usb_composite_device.c @@ -179,7 +179,7 @@ void WakeUpHost(void) { } // Send resume signal - this will call USB_DeviceKhciControl(khciHandle, kUSB_DeviceControlResume, NULL); USB_DeviceSetStatus(UsbCompositeDevice.deviceHandle, kUSB_DeviceStatusBus, NULL); - while (CurrentPowerMode != PowerMode_Awake) { + while (CurrentPowerMode > PowerMode_LastAwake) { ; } } diff --git a/right/src/usb_report_updater.c b/right/src/usb_report_updater.c index 5eba9bc8..556e6cb9 100644 --- a/right/src/usb_report_updater.c +++ b/right/src/usb_report_updater.c @@ -563,7 +563,7 @@ static void updateActionStates() { // as it is pressed actionCache[slotId][keyId].modifierLayerMask = 0; - if (CurrentPowerMode != PowerMode_Awake && CurrentPowerMode <= PowerMode_LightSleep) { + if (CurrentPowerMode > PowerMode_LastAwake && CurrentPowerMode <= PowerMode_LightSleep) { PowerMode_WakeHost(); PowerMode_ActivateMode(PowerMode_Awake, false); } @@ -797,7 +797,7 @@ void UpdateUsbReports(void) } if (EventVector_IsSet(EventVector_SendUsbReports | EventVector_ResendUsbReports)) { - if (CurrentPowerMode < PowerMode_DeepSleep) { + if (CurrentPowerMode < PowerMode_Lock) { mergeReports(); sendActiveReports(); } else { diff --git a/right/src/wormhole.c b/right/src/wormhole.c new file mode 100644 index 00000000..f7e854f3 --- /dev/null +++ b/right/src/wormhole.c @@ -0,0 +1,15 @@ +#include "wormhole.h" + + +#ifdef __ZEPHYR__ + +#include +__noinit wormhole_data_t Wormhole; + +#else + +#include "attributes.h" +ATTR_NO_INIT wormhole_data_t Wormhole; + +#endif + diff --git a/right/src/wormhole.h b/right/src/wormhole.h new file mode 100644 index 00000000..532907a7 --- /dev/null +++ b/right/src/wormhole.h @@ -0,0 +1,25 @@ +#ifndef __WORMHOLE_RUNTIME_H__ +#define __WORMHOLE_RUNTIME_H__ + +// Includes: + + #include + #include + #include "power_mode.h" + + +// Macros: + +// Typedefs: + + typedef struct { + power_mode_t restartPowerMode; + } wormhole_data_t; + +// Variables: + + extern wormhole_data_t Wormhole; + +// Functions: + +#endif