system76_ec.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. /*
  2. * Copyright (C) 2021 System76
  3. * Copyright (C) 2021 Jimmy Cassis <KernelOops@outlook.com>
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. #include <string.h>
  19. #include "dynamic_keymap.h"
  20. #include "raw_hid.h"
  21. #include "rgb_matrix.h"
  22. #include "version.h"
  23. enum Command {
  24. CMD_PROBE = 1, // Probe for System76 EC protocol
  25. CMD_BOARD = 2, // Read board string
  26. CMD_VERSION = 3, // Read version string
  27. CMD_RESET = 6, // Reset to bootloader
  28. CMD_KEYMAP_GET = 9, // Get keyboard map index
  29. CMD_KEYMAP_SET = 10, // Set keyboard map index
  30. CMD_LED_GET_VALUE = 11, // Get LED value by index
  31. CMD_LED_SET_VALUE = 12, // Set LED value by index
  32. CMD_LED_GET_COLOR = 13, // Get LED color by index
  33. CMD_LED_SET_COLOR = 14, // Set LED color by index
  34. CMD_LED_GET_MODE = 15, // Get LED matrix mode and speed
  35. CMD_LED_SET_MODE = 16, // Set LED matrix mode and speed
  36. CMD_MATRIX_GET = 17, // Get currently pressed keys
  37. CMD_LED_SAVE = 18, // Save LED settings to ROM
  38. CMD_SET_NO_INPUT = 19, // Enable/disable no input mode
  39. };
  40. bool input_disabled = false;
  41. #define CMD_LED_INDEX_ALL 0xFF
  42. static bool keymap_get(uint8_t layer, uint8_t output, uint8_t input, uint16_t *value) {
  43. if (layer < dynamic_keymap_get_layer_count()) {
  44. if (output < MATRIX_ROWS) {
  45. if (input < MATRIX_COLS) {
  46. *value = dynamic_keymap_get_keycode(layer, output, input);
  47. return true;
  48. }
  49. }
  50. }
  51. return false;
  52. }
  53. static bool keymap_set(uint8_t layer, uint8_t output, uint8_t input, uint16_t value) {
  54. if (layer < dynamic_keymap_get_layer_count()) {
  55. if (output < MATRIX_ROWS) {
  56. if (input < MATRIX_COLS) {
  57. dynamic_keymap_set_keycode(layer, output, input, value);
  58. return true;
  59. }
  60. }
  61. }
  62. return false;
  63. }
  64. static bool bootloader_reset = false;
  65. static bool bootloader_unlocked = false;
  66. void system76_ec_unlock(void) {
  67. #ifdef RGB_MATRIX_CUSTOM_KB
  68. rgb_matrix_mode_noeeprom(RGB_MATRIX_CUSTOM_unlocked);
  69. #endif
  70. #ifdef SYSTEM76_EC
  71. bootloader_unlocked = true;
  72. #endif
  73. }
  74. bool system76_ec_is_unlocked(void) { return bootloader_unlocked; }
  75. #ifdef RGB_MATRIX_CUSTOM_KB
  76. enum Mode {
  77. MODE_SOLID_COLOR = 0,
  78. MODE_PER_KEY,
  79. #ifndef DISABLE_RGB_MATRIX_ANIMATIONS
  80. MODE_CYCLE_ALL,
  81. MODE_CYCLE_LEFT_RIGHT,
  82. MODE_CYCLE_UP_DOWN,
  83. MODE_CYCLE_OUT_IN,
  84. MODE_CYCLE_OUT_IN_DUAL,
  85. MODE_RAINBOW_MOVING_CHEVRON,
  86. MODE_CYCLE_PINWHEEL,
  87. MODE_CYCLE_SPIRAL,
  88. MODE_RAINDROPS,
  89. MODE_SPLASH,
  90. MODE_MULTISPLASH,
  91. #endif // DISABLE_RGB_MATRIX_ANIMATIONS
  92. MODE_ACTIVE_KEYS,
  93. MODE_DISABLED,
  94. MODE_LAST,
  95. };
  96. // clang-format off
  97. static enum rgb_matrix_effects mode_map[] = {
  98. RGB_MATRIX_SOLID_COLOR,
  99. RGB_MATRIX_CUSTOM_raw_rgb,
  100. #ifndef DISABLE_RGB_MATRIX_ANIMATIONS
  101. RGB_MATRIX_CYCLE_ALL,
  102. RGB_MATRIX_CYCLE_LEFT_RIGHT,
  103. RGB_MATRIX_CYCLE_UP_DOWN,
  104. RGB_MATRIX_CYCLE_OUT_IN,
  105. RGB_MATRIX_CYCLE_OUT_IN_DUAL,
  106. RGB_MATRIX_RAINBOW_MOVING_CHEVRON,
  107. RGB_MATRIX_CYCLE_PINWHEEL,
  108. RGB_MATRIX_CYCLE_SPIRAL,
  109. RGB_MATRIX_RAINDROPS,
  110. RGB_MATRIX_SPLASH,
  111. RGB_MATRIX_MULTISPLASH,
  112. #endif // DISABLE_RGB_MATRIX_ANIMATIONS
  113. RGB_MATRIX_CUSTOM_active_keys,
  114. RGB_MATRIX_NONE,
  115. };
  116. // clang-format on
  117. _Static_assert(sizeof(mode_map) == MODE_LAST, "mode_map_length");
  118. RGB raw_rgb_data[RGB_MATRIX_LED_COUNT];
  119. // clang-format off
  120. rgb_config_t layer_rgb[DYNAMIC_KEYMAP_LAYER_COUNT] = {
  121. // Layer 0
  122. {
  123. .enable = 1,
  124. .mode = RGB_MATRIX_DEFAULT_MODE,
  125. .hsv = {
  126. .h = RGB_MATRIX_DEFAULT_HUE,
  127. .s = RGB_MATRIX_DEFAULT_SAT,
  128. .v = RGB_MATRIX_DEFAULT_VAL,
  129. },
  130. .speed = RGB_MATRIX_DEFAULT_SPD,
  131. .flags = LED_FLAG_KEYLIGHT,
  132. },
  133. // Layer 1
  134. {
  135. .enable = 1,
  136. .mode = RGB_MATRIX_CUSTOM_active_keys,
  137. .hsv = {
  138. .h = RGB_MATRIX_DEFAULT_HUE,
  139. .s = RGB_MATRIX_DEFAULT_SAT,
  140. .v = RGB_MATRIX_DEFAULT_VAL,
  141. },
  142. .speed = RGB_MATRIX_DEFAULT_SPD,
  143. .flags = LED_FLAG_KEYLIGHT,
  144. },
  145. // Layer 2
  146. {
  147. .enable = 1,
  148. .mode = RGB_MATRIX_CUSTOM_active_keys,
  149. .hsv = {
  150. .h = RGB_MATRIX_DEFAULT_HUE,
  151. .s = RGB_MATRIX_DEFAULT_SAT,
  152. .v = RGB_MATRIX_DEFAULT_VAL,
  153. },
  154. .speed = RGB_MATRIX_DEFAULT_SPD,
  155. .flags = LED_FLAG_KEYLIGHT,
  156. },
  157. // Layer 3
  158. {
  159. .enable = 1,
  160. .mode = RGB_MATRIX_CUSTOM_active_keys,
  161. .hsv = {
  162. .h = RGB_MATRIX_DEFAULT_HUE,
  163. .s = RGB_MATRIX_DEFAULT_SAT,
  164. .v = RGB_MATRIX_DEFAULT_VAL,
  165. },
  166. .speed = RGB_MATRIX_DEFAULT_SPD,
  167. .flags = LED_FLAG_KEYLIGHT,
  168. },
  169. };
  170. // clang-format on
  171. // Read or write EEPROM data with checks for being inside System76 EC region.
  172. static bool system76_ec_eeprom_op(void *buf, uint16_t size, uint16_t offset, bool write) {
  173. uint16_t addr = SYSTEM76_EC_EEPROM_ADDR + offset;
  174. uint16_t end = addr + size;
  175. // Check for overflow and zero size
  176. if ((end > addr) && (addr >= SYSTEM76_EC_EEPROM_ADDR) && (end <= (SYSTEM76_EC_EEPROM_ADDR + SYSTEM76_EC_EEPROM_SIZE))) {
  177. if (write) {
  178. eeprom_update_block((const void *)buf, (void *)addr, size);
  179. } else {
  180. eeprom_read_block((void *)buf, (const void *)addr, size);
  181. }
  182. return true;
  183. } else {
  184. return false;
  185. }
  186. }
  187. // Read or write EEPROM RGB parameters.
  188. void system76_ec_rgb_eeprom(bool write) {
  189. uint16_t layer_rgb_size = sizeof(layer_rgb);
  190. system76_ec_eeprom_op((void *)layer_rgb, layer_rgb_size, 0, write);
  191. system76_ec_eeprom_op((void *)raw_rgb_data, sizeof(raw_rgb_data), layer_rgb_size, write);
  192. }
  193. // Update RGB parameters on layer change.
  194. void system76_ec_rgb_layer(layer_state_t layer_state) {
  195. if (!bootloader_unlocked) {
  196. uint8_t layer = get_highest_layer(layer_state);
  197. if (layer < DYNAMIC_KEYMAP_LAYER_COUNT) {
  198. rgb_matrix_config = layer_rgb[layer];
  199. }
  200. }
  201. }
  202. #endif // RGB_MATRIX_CUSTOM_KB
  203. void raw_hid_receive(uint8_t *data, uint8_t length) {
  204. // Error response by default, set to success by commands
  205. data[1] = 1;
  206. switch (data[0]) {
  207. case CMD_PROBE:
  208. // Signature
  209. data[2] = 0x76;
  210. data[3] = 0xEC;
  211. // Version
  212. data[4] = 0x01;
  213. data[1] = 0;
  214. break;
  215. case CMD_BOARD:
  216. strncpy((char *)&data[2], QMK_KEYBOARD, length - 2);
  217. data[1] = 0;
  218. break;
  219. case CMD_VERSION:
  220. strncpy((char *)&data[2], QMK_VERSION, length - 2);
  221. data[1] = 0;
  222. break;
  223. case CMD_RESET:
  224. if (bootloader_unlocked) {
  225. data[1] = 0;
  226. bootloader_reset = true;
  227. }
  228. break;
  229. case CMD_KEYMAP_GET: {
  230. uint16_t value = 0;
  231. if (keymap_get(data[2], data[3], data[4], &value)) {
  232. data[5] = (uint8_t)value;
  233. data[6] = (uint8_t)(value >> 8);
  234. data[1] = 0;
  235. }
  236. } break;
  237. case CMD_KEYMAP_SET: {
  238. uint16_t value = ((uint16_t)data[5]) | (((uint16_t)data[6]) << 8);
  239. if (keymap_set(data[2], data[3], data[4], value)) {
  240. data[1] = 0;
  241. }
  242. } break;
  243. #ifdef RGB_MATRIX_CUSTOM_KB
  244. case CMD_LED_GET_VALUE:
  245. if (!bootloader_unlocked) {
  246. uint8_t index = data[2];
  247. for (uint8_t layer = 0; layer < DYNAMIC_KEYMAP_LAYER_COUNT; layer++) {
  248. if (index == (0xF0 | layer)) {
  249. data[3] = layer_rgb[layer].hsv.v;
  250. data[4] = RGB_MATRIX_MAXIMUM_BRIGHTNESS;
  251. data[1] = 0;
  252. break;
  253. }
  254. }
  255. }
  256. break;
  257. case CMD_LED_SET_VALUE:
  258. if (!bootloader_unlocked) {
  259. uint8_t index = data[2];
  260. uint8_t value = data[3];
  261. if (value >= RGB_MATRIX_MAXIMUM_BRIGHTNESS) {
  262. value = RGB_MATRIX_MAXIMUM_BRIGHTNESS;
  263. }
  264. for (uint8_t layer = 0; layer < DYNAMIC_KEYMAP_LAYER_COUNT; layer++) {
  265. if (index == (0xF0 | layer)) {
  266. layer_rgb[layer].hsv.v = value;
  267. data[1] = 0;
  268. system76_ec_rgb_layer(layer_state);
  269. break;
  270. }
  271. }
  272. }
  273. break;
  274. case CMD_LED_GET_COLOR:
  275. if (!bootloader_unlocked) {
  276. uint8_t index = data[2];
  277. if (index < RGB_MATRIX_LED_COUNT) {
  278. data[3] = raw_rgb_data[index].r;
  279. data[4] = raw_rgb_data[index].g;
  280. data[5] = raw_rgb_data[index].b;
  281. data[1] = 0;
  282. } else {
  283. for (uint8_t layer = 0; layer < DYNAMIC_KEYMAP_LAYER_COUNT; layer++) {
  284. if (index == (0xF0 | layer)) {
  285. data[3] = layer_rgb[layer].hsv.h;
  286. data[4] = layer_rgb[layer].hsv.s;
  287. data[5] = 0;
  288. data[1] = 0;
  289. break;
  290. }
  291. }
  292. }
  293. }
  294. break;
  295. case CMD_LED_SET_COLOR:
  296. if (!bootloader_unlocked) {
  297. uint8_t index = data[2];
  298. RGB rgb = {
  299. .r = data[3],
  300. .g = data[4],
  301. .b = data[5],
  302. };
  303. if (index < RGB_MATRIX_LED_COUNT) {
  304. raw_rgb_data[index] = rgb;
  305. data[1] = 0;
  306. } else {
  307. for (uint8_t layer = 0; layer < DYNAMIC_KEYMAP_LAYER_COUNT; layer++) {
  308. if (index == (0xF0 | layer)) {
  309. layer_rgb[layer].hsv.h = rgb.r;
  310. layer_rgb[layer].hsv.s = rgb.g;
  311. // Ignore rgb.b
  312. data[1] = 0;
  313. system76_ec_rgb_layer(layer_state);
  314. break;
  315. }
  316. }
  317. }
  318. }
  319. break;
  320. case CMD_LED_GET_MODE:
  321. if (!bootloader_unlocked) {
  322. uint8_t layer = data[2];
  323. if (layer < DYNAMIC_KEYMAP_LAYER_COUNT) {
  324. enum rgb_matrix_effects mode = layer_rgb[layer].mode;
  325. for (uint8_t i = 0; i < MODE_LAST; i++) {
  326. if (mode_map[i] == mode) {
  327. data[3] = i;
  328. data[4] = layer_rgb[layer].speed;
  329. data[1] = 0;
  330. break;
  331. }
  332. }
  333. }
  334. }
  335. break;
  336. case CMD_LED_SET_MODE:
  337. if (!bootloader_unlocked) {
  338. uint8_t layer = data[2];
  339. uint8_t mode = data[3];
  340. uint8_t speed = data[4];
  341. if (layer < DYNAMIC_KEYMAP_LAYER_COUNT && mode < MODE_LAST) {
  342. layer_rgb[layer].mode = mode_map[mode];
  343. layer_rgb[layer].speed = speed;
  344. data[1] = 0;
  345. system76_ec_rgb_layer(layer_state);
  346. }
  347. }
  348. break;
  349. case CMD_LED_SAVE:
  350. if (!bootloader_unlocked) {
  351. system76_ec_rgb_eeprom(true);
  352. data[1] = 0;
  353. }
  354. break;
  355. #endif // RGB_MATRIX_CUSTOM_KB
  356. case CMD_MATRIX_GET: {
  357. // TODO: Improve performance?
  358. data[2] = matrix_rows();
  359. data[3] = matrix_cols();
  360. uint8_t byte = 4;
  361. uint8_t bit = 0;
  362. for (uint8_t row = 0; row < matrix_rows(); row++) {
  363. for (uint8_t col = 0; col < matrix_cols(); col++) {
  364. if (byte < length) {
  365. if (matrix_is_on(row, col)) {
  366. data[byte] |= (1 << bit);
  367. } else {
  368. data[byte] &= ~(1 << bit);
  369. }
  370. }
  371. bit++;
  372. if (bit >= 8) {
  373. byte++;
  374. bit = 0;
  375. }
  376. }
  377. }
  378. data[1] = 0;
  379. } break;
  380. case CMD_SET_NO_INPUT: {
  381. clear_keyboard();
  382. input_disabled = data[2] != 0;
  383. data[1] = 0;
  384. } break;
  385. }
  386. raw_hid_send(data, length);
  387. if (bootloader_reset) {
  388. // Give host time to read response
  389. wait_ms(100);
  390. // Jump to the bootloader
  391. bootloader_jump();
  392. }
  393. }