theseus75.c 8.3 KB

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