aboutsummaryrefslogtreecommitdiffstats
path: root/keyboards/haverworks/theseus75/theseus75.c
diff options
context:
space:
mode:
Diffstat (limited to 'keyboards/haverworks/theseus75/theseus75.c')
-rw-r--r--keyboards/haverworks/theseus75/theseus75.c214
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