qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

theseus75.c (8488B)


      1 // Copyright 2023 Moritz Plattner (@ebastler), Alex Havermale (@haversnail)
      2 // SPDX-License-Identifier: GPL-2.0-or-later
      3 
      4 #include "quantum.h"
      5 #include "transactions.h"
      6 #include <stdio.h>
      7 #include "print.h"
      8 #include "split_util.h"
      9 #include "usbpd.h"
     10 
     11 typedef struct _kb_state_t {
     12     usbpd_allowance_t allowance;
     13 } kb_state_t;
     14 
     15 kb_state_t kb_state;
     16 
     17 const char* usbpd_str(usbpd_allowance_t allowance) {
     18     switch (allowance) {
     19         case USBPD_500MA:
     20             return "500mA";
     21         case USBPD_1500MA:
     22             return "1500mA";
     23         case USBPD_3000MA:
     24             return "3000mA";
     25         default:
     26             dprintf("Encountered unknown allowance enum value: %d\n", allowance);
     27             return "UNKNOWN";
     28     }
     29 }
     30 
     31 void kb_state_slave_handler(uint8_t m2s_size, const void* m2s_buffer, uint8_t s2m_size, void* s2m_buffer) {
     32     if (m2s_size == sizeof(kb_state_t)) {
     33         memcpy(&kb_state, m2s_buffer, sizeof(kb_state_t));
     34     } else {
     35         dprintf("Unexpected response in slave handler\n"); // TODO: add split debug logging
     36     }
     37 }
     38 
     39 void keyboard_pre_init_kb(void) {
     40     // Disable the PD peripheral in pre-init because its pins are being used in the matrix:
     41     PWR->CR3 |= PWR_CR3_UCPD_DBDIS;
     42     // Call the corresponding _user() function (see https://docs.qmk.fm/#/custom_quantum_functions)
     43     keyboard_pre_init_user();
     44 }
     45 
     46 void keyboard_post_init_kb(void) {
     47     // Register keyboard state transaction:
     48     transaction_register_rpc(RPC_ID_KB_STATE, kb_state_slave_handler);
     49 
     50     // Set default state values:
     51     kb_state.allowance = USBPD_500MA;
     52 
     53     // If the keyboard is master,
     54     if (is_keyboard_master()) {
     55         // Turn on power to the split half and to underglow LEDs:
     56         gpio_set_pin_output(PSW_PIN);
     57         gpio_write_pin_high(PSW_PIN);
     58 
     59         // Enable inputs used for current negotiation:
     60         gpio_set_pin_input_high(USBPD_1_PIN);
     61         gpio_set_pin_input_high(USBPD_2_PIN);
     62 
     63         // Not needed in this mode (always high-Z with pull-up on PCB if port controller is sink)
     64         gpio_set_pin_input_high(ID_PIN);
     65     } else {
     66         // Prepare output to enable power for USB output after negotiation:
     67         gpio_set_pin_output(PSW_PIN);
     68 
     69         // Switch the USB MUXes between hub and ports:
     70         gpio_set_pin_output(USBSW_PIN);
     71         gpio_write_pin_high(USBSW_PIN);
     72 
     73         // Enable outputs used for current negotiation and default to 500mA:
     74         gpio_set_pin_output(USBPD_1_PIN);
     75         gpio_write_pin_high(USBPD_1_PIN);
     76         gpio_set_pin_output(USBPD_2_PIN);
     77         gpio_write_pin_high(USBPD_2_PIN);
     78 
     79         // Use ID pin to check if client is detected (if low: USB source port powered):
     80         gpio_set_pin_input_high(ID_PIN);
     81 
     82         // Indicate that the hub is either self-powered or bus-powered based on whether the bus-power mode flag is enabled
     83         // (configurable for users who would rather always have their device connect, regardless of whether they meet the specs):
     84         gpio_set_pin_output(BUS_B_PIN);
     85         if (DISABLE_BUS_POWER_MODE == TRUE) {
     86             gpio_write_pin_high(BUS_B_PIN);
     87         } else {
     88             gpio_write_pin_low(BUS_B_PIN);
     89         }
     90     }
     91     // Call the corresponding _user() function (see https://docs.qmk.fm/#/custom_quantum_functions)
     92     keyboard_post_init_user();
     93 }
     94 
     95 void housekeeping_task_kb(void) {
     96     // Update any shared kb state to send to slave:
     97     static uint32_t last_usbpd_allowance_check_time = 0;
     98     if (timer_elapsed32(last_usbpd_allowance_check_time) > USBPD_ALLOWANCE_CHECK_INTERVAL) {
     99         // On master side: check USBPD_1_PIN and USBPD_2_PIN to determine current negotiated with host
    100         // (Can't use the usbpd_get_allowance() function, as this uses this uses the native CC PD interface
    101         // of the G series MCU, while we're using dedicated port controllers instead):
    102         if (is_keyboard_master()) {
    103             usbpd_allowance_t allowance;
    104 
    105             if (gpio_read_pin(USBPD_1_PIN)) {
    106                 allowance = USBPD_500MA;
    107             } else if (gpio_read_pin(USBPD_2_PIN)) {
    108                 allowance = USBPD_1500MA;
    109             } else {
    110                 allowance = USBPD_3000MA;
    111             }
    112 
    113             if (kb_state.allowance != allowance) {
    114                 printf("Host negotiated current: %s -> %s\n", usbpd_str(kb_state.allowance), usbpd_str(allowance));
    115                 kb_state.allowance = allowance;
    116             }
    117         } else {
    118             // On peripheral side - If ID_PIN is low: USB client negotiated 5V successfully -> enable power routing
    119             // Check if PSW_PIN is not already high to avoid wasting time
    120             if (!gpio_read_pin(ID_PIN) && !gpio_read_pin(PSW_PIN)) {
    121                 gpio_write_pin_high(PSW_PIN);
    122                 dprintf("USB downstream device connected\n"); // TODO: add split debug logging
    123             } else if (gpio_read_pin(ID_PIN) && gpio_read_pin(PSW_PIN)) {
    124                 gpio_write_pin_low(PSW_PIN);
    125                 dprintf("USB downstream device disconnected\n"); // TODO: add split debug logging
    126             }
    127         };
    128         last_usbpd_allowance_check_time = timer_read32();
    129     };
    130 
    131     // Sync state from master to slave:
    132     if (is_keyboard_master() && is_transport_connected()) {
    133         bool              needs_sync = false;
    134         static uint32_t   last_kb_state_sync_time;
    135         static kb_state_t last_kb_state;
    136 
    137         // Check if the state values are different:
    138         if (memcmp(&kb_state, &last_kb_state, sizeof(kb_state_t))) {
    139             needs_sync = true;
    140             memcpy(&last_kb_state, &kb_state, sizeof(kb_state_t));
    141         }
    142 
    143         // Sync state every so often regardless:
    144         if (timer_elapsed32(last_kb_state_sync_time) > KB_STATE_SYNC_INTERVAL) {
    145             needs_sync = true;
    146         }
    147 
    148         if (needs_sync) {
    149             bool did_sync = transaction_rpc_send(RPC_ID_KB_STATE, sizeof(kb_state_t), &kb_state);
    150             if (did_sync) {
    151                 dprintf("Synced to slave\n");
    152                 last_kb_state_sync_time = timer_read32();
    153             } else {
    154                 dprintf("Failed to sync state\n");
    155             }
    156         }
    157     }
    158 
    159     // Update the USBPD output pins on slave half whenever allowance has changed:
    160     if (!is_keyboard_master()) {
    161         static usbpd_allowance_t last_allowance;
    162 
    163         if (last_allowance != kb_state.allowance) {
    164             last_allowance = kb_state.allowance;
    165 
    166             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
    167 
    168             switch (kb_state.allowance) {
    169                 default:
    170                 case USBPD_500MA:
    171                     // Set USBPD output to 500 mA:
    172                     gpio_write_pin_high(USBPD_1_PIN);
    173                     gpio_write_pin_high(USBPD_2_PIN);
    174                     if (DISABLE_BUS_POWER_MODE == TRUE) {
    175                         // Indicate hub is self-powered and devices can try to connect or fast charge:
    176                         gpio_write_pin_high(BUS_B_PIN);
    177                     } else {
    178                         // Indicate hub is bus-powered and devices should not try to connect or fast charge:
    179                         gpio_write_pin_low(BUS_B_PIN);
    180                     }
    181                     break;
    182                 case USBPD_1500MA:
    183                     // Set USBPD output to 500 mA:
    184                     gpio_write_pin_high(USBPD_1_PIN);
    185                     gpio_write_pin_high(USBPD_2_PIN);
    186                     // Indicate hub is self-powered and devices can try to connect or fast charge:
    187                     gpio_write_pin_high(BUS_B_PIN);
    188                     break;
    189                 case USBPD_3000MA:
    190                     // Set USBPD output to 1500 mA:
    191                     gpio_write_pin_low(USBPD_1_PIN);
    192                     gpio_write_pin_high(USBPD_2_PIN);
    193                     // Indicate hub is self-powered and devices can try to connect or fast charge:
    194                     gpio_write_pin_high(BUS_B_PIN);
    195                     break;
    196             }
    197         }
    198     }
    199 }
    200 
    201 #ifdef RGB_MATRIX_ENABLE
    202 bool rgb_matrix_indicators_kb(void) {
    203     if (!rgb_matrix_indicators_user()) {
    204         return false;
    205     }
    206 
    207     if (host_keyboard_led_state().caps_lock) {
    208         rgb_matrix_set_color(CAPS_LOCK_LED_INDEX, INDICATOR_MAX_BRIGHTNESS, INDICATOR_MAX_BRIGHTNESS, INDICATOR_MAX_BRIGHTNESS);
    209     } else {
    210         rgb_matrix_set_color(CAPS_LOCK_LED_INDEX, 0, 0, 0);
    211     }
    212     return true;
    213 }
    214 #endif