| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- // 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
|