diff options
Diffstat (limited to 'keyboards/haverworks/theseus75/theseus75.c')
| -rw-r--r-- | keyboards/haverworks/theseus75/theseus75.c | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/keyboards/haverworks/theseus75/theseus75.c b/keyboards/haverworks/theseus75/theseus75.c new file mode 100644 index 0000000000..45ead4811d --- /dev/null +++ b/keyboards/haverworks/theseus75/theseus75.c @@ -0,0 +1,214 @@ +// Copyright 2023 Moritz Plattner (@ebastler), Alex Havermale (@haversnail) +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "quantum.h" +#include "transactions.h" +#include <stdio.h> +#include "print.h" +#include "split_util.h" +#include "usbpd.h" + +typedef struct _kb_state_t { + usbpd_allowance_t allowance; +} kb_state_t; + +kb_state_t kb_state; + +const char* usbpd_str(usbpd_allowance_t allowance) { + switch (allowance) { + case USBPD_500MA: + return "500mA"; + case USBPD_1500MA: + return "1500mA"; + case USBPD_3000MA: + return "3000mA"; + default: + dprintf("Encountered unknown allowance enum value: %d\n", allowance); + return "UNKNOWN"; + } +} + +void kb_state_slave_handler(uint8_t m2s_size, const void* m2s_buffer, uint8_t s2m_size, void* s2m_buffer) { + if (m2s_size == sizeof(kb_state_t)) { + memcpy(&kb_state, m2s_buffer, sizeof(kb_state_t)); + } else { + dprintf("Unexpected response in slave handler\n"); // TODO: add split debug logging + } +} + +void keyboard_pre_init_kb(void) { + // Disable the PD peripheral in pre-init because its pins are being used in the matrix: + PWR->CR3 |= PWR_CR3_UCPD_DBDIS; + // Call the corresponding _user() function (see https://docs.qmk.fm/#/custom_quantum_functions) + keyboard_pre_init_user(); +} + +void keyboard_post_init_kb(void) { + // Register keyboard state transaction: + transaction_register_rpc(RPC_ID_KB_STATE, kb_state_slave_handler); + + // Set default state values: + kb_state.allowance = USBPD_500MA; + + // If the keyboard is master, + if (is_keyboard_master()) { + // Turn on power to the split half and to underglow LEDs: + gpio_set_pin_output(PSW_PIN); + gpio_write_pin_high(PSW_PIN); + + // Enable inputs used for current negotiation: + gpio_set_pin_input_high(USBPD_1_PIN); + gpio_set_pin_input_high(USBPD_2_PIN); + + // Not needed in this mode (always high-Z with pull-up on PCB if port controller is sink) + gpio_set_pin_input_high(ID_PIN); + } else { + // Prepare output to enable power for USB output after negotiation: + gpio_set_pin_output(PSW_PIN); + + // Switch the USB MUXes between hub and ports: + gpio_set_pin_output(USBSW_PIN); + gpio_write_pin_high(USBSW_PIN); + + // Enable outputs used for current negotiation and default to 500mA: + gpio_set_pin_output(USBPD_1_PIN); + gpio_write_pin_high(USBPD_1_PIN); + gpio_set_pin_output(USBPD_2_PIN); + gpio_write_pin_high(USBPD_2_PIN); + + // Use ID pin to check if client is detected (if low: USB source port powered): + gpio_set_pin_input_high(ID_PIN); + + // Indicate that the hub is either self-powered or bus-powered based on whether the bus-power mode flag is enabled + // (configurable for users who would rather always have their device connect, regardless of whether they meet the specs): + gpio_set_pin_output(BUS_B_PIN); + if (DISABLE_BUS_POWER_MODE == TRUE) { + gpio_write_pin_high(BUS_B_PIN); + } else { + gpio_write_pin_low(BUS_B_PIN); + } + } + // Call the corresponding _user() function (see https://docs.qmk.fm/#/custom_quantum_functions) + keyboard_post_init_user(); +} + +void housekeeping_task_kb(void) { + // Update any shared kb state to send to slave: + static uint32_t last_usbpd_allowance_check_time = 0; + if (timer_elapsed32(last_usbpd_allowance_check_time) > USBPD_ALLOWANCE_CHECK_INTERVAL) { + // On master side: check USBPD_1_PIN and USBPD_2_PIN to determine current negotiated with host + // (Can't use the usbpd_get_allowance() function, as this uses this uses the native CC PD interface + // of the G series MCU, while we're using dedicated port controllers instead): + if (is_keyboard_master()) { + usbpd_allowance_t allowance; + + if (gpio_read_pin(USBPD_1_PIN)) { + allowance = USBPD_500MA; + } else if (gpio_read_pin(USBPD_2_PIN)) { + allowance = USBPD_1500MA; + } else { + allowance = USBPD_3000MA; + } + + if (kb_state.allowance != allowance) { + printf("Host negotiated current: %s -> %s\n", usbpd_str(kb_state.allowance), usbpd_str(allowance)); + kb_state.allowance = allowance; + } + } else { + // On peripheral side - If ID_PIN is low: USB client negotiated 5V successfully -> enable power routing + // Check if PSW_PIN is not already high to avoid wasting time + if (!gpio_read_pin(ID_PIN) && !gpio_read_pin(PSW_PIN)) { + gpio_write_pin_high(PSW_PIN); + dprintf("USB downstream device connected\n"); // TODO: add split debug logging + } else if (gpio_read_pin(ID_PIN) && gpio_read_pin(PSW_PIN)) { + gpio_write_pin_low(PSW_PIN); + dprintf("USB downstream device disconnected\n"); // TODO: add split debug logging + } + }; + last_usbpd_allowance_check_time = timer_read32(); + }; + + // Sync state from master to slave: + if (is_keyboard_master() && is_transport_connected()) { + bool needs_sync = false; + static uint32_t last_kb_state_sync_time; + static kb_state_t last_kb_state; + + // Check if the state values are different: + if (memcmp(&kb_state, &last_kb_state, sizeof(kb_state_t))) { + needs_sync = true; + memcpy(&last_kb_state, &kb_state, sizeof(kb_state_t)); + } + + // Sync state every so often regardless: + if (timer_elapsed32(last_kb_state_sync_time) > KB_STATE_SYNC_INTERVAL) { + needs_sync = true; + } + + if (needs_sync) { + bool did_sync = transaction_rpc_send(RPC_ID_KB_STATE, sizeof(kb_state_t), &kb_state); + if (did_sync) { + dprintf("Synced to slave\n"); + last_kb_state_sync_time = timer_read32(); + } else { + dprintf("Failed to sync state\n"); + } + } + } + + // Update the USBPD output pins on slave half whenever allowance has changed: + if (!is_keyboard_master()) { + static usbpd_allowance_t last_allowance; + + if (last_allowance != kb_state.allowance) { + last_allowance = kb_state.allowance; + + printf("Setting USB-PD output to %s (%s-powered)\n", usbpd_str(kb_state.allowance), kb_state.allowance == USBPD_500MA ? "bus" : "self"); // TODO: add split debug logging + + switch (kb_state.allowance) { + default: + case USBPD_500MA: + // Set USBPD output to 500 mA: + gpio_write_pin_high(USBPD_1_PIN); + gpio_write_pin_high(USBPD_2_PIN); + if (DISABLE_BUS_POWER_MODE == TRUE) { + // Indicate hub is self-powered and devices can try to connect or fast charge: + gpio_write_pin_high(BUS_B_PIN); + } else { + // Indicate hub is bus-powered and devices should not try to connect or fast charge: + gpio_write_pin_low(BUS_B_PIN); + } + break; + case USBPD_1500MA: + // Set USBPD output to 500 mA: + gpio_write_pin_high(USBPD_1_PIN); + gpio_write_pin_high(USBPD_2_PIN); + // Indicate hub is self-powered and devices can try to connect or fast charge: + gpio_write_pin_high(BUS_B_PIN); + break; + case USBPD_3000MA: + // Set USBPD output to 1500 mA: + gpio_write_pin_low(USBPD_1_PIN); + gpio_write_pin_high(USBPD_2_PIN); + // Indicate hub is self-powered and devices can try to connect or fast charge: + gpio_write_pin_high(BUS_B_PIN); + break; + } + } + } +} + +#ifdef RGB_MATRIX_ENABLE +bool rgb_matrix_indicators_kb(void) { + if (!rgb_matrix_indicators_user()) { + return false; + } + + if (host_keyboard_led_state().caps_lock) { + rgb_matrix_set_color(CAPS_LOCK_LED_INDEX, INDICATOR_MAX_BRIGHTNESS, INDICATOR_MAX_BRIGHTNESS, INDICATOR_MAX_BRIGHTNESS); + } else { + rgb_matrix_set_color(CAPS_LOCK_LED_INDEX, 0, 0, 0); + } + return true; +} +#endif |