system76_ec.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  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. MODE_CYCLE_ALL,
  80. MODE_CYCLE_LEFT_RIGHT,
  81. MODE_CYCLE_UP_DOWN,
  82. MODE_CYCLE_OUT_IN,
  83. MODE_CYCLE_OUT_IN_DUAL,
  84. MODE_RAINBOW_MOVING_CHEVRON,
  85. MODE_CYCLE_PINWHEEL,
  86. MODE_CYCLE_SPIRAL,
  87. MODE_RAINDROPS,
  88. MODE_SPLASH,
  89. MODE_MULTISPLASH,
  90. MODE_ACTIVE_KEYS,
  91. MODE_DISABLED,
  92. MODE_LAST,
  93. };
  94. // clang-format off
  95. static enum rgb_matrix_effects mode_map[] = {
  96. RGB_MATRIX_SOLID_COLOR,
  97. RGB_MATRIX_CUSTOM_raw_rgb,
  98. RGB_MATRIX_CYCLE_ALL,
  99. RGB_MATRIX_CYCLE_LEFT_RIGHT,
  100. RGB_MATRIX_CYCLE_UP_DOWN,
  101. RGB_MATRIX_CYCLE_OUT_IN,
  102. RGB_MATRIX_CYCLE_OUT_IN_DUAL,
  103. RGB_MATRIX_RAINBOW_MOVING_CHEVRON,
  104. RGB_MATRIX_CYCLE_PINWHEEL,
  105. RGB_MATRIX_CYCLE_SPIRAL,
  106. RGB_MATRIX_RAINDROPS,
  107. RGB_MATRIX_SPLASH,
  108. RGB_MATRIX_MULTISPLASH,
  109. RGB_MATRIX_CUSTOM_active_keys,
  110. RGB_MATRIX_NONE,
  111. };
  112. // clang-format on
  113. _Static_assert(sizeof(mode_map) == MODE_LAST, "mode_map_length");
  114. RGB raw_rgb_data[DRIVER_LED_TOTAL];
  115. // clang-format off
  116. rgb_config_t layer_rgb[DYNAMIC_KEYMAP_LAYER_COUNT] = {
  117. // Layer 0
  118. {
  119. .enable = 1,
  120. .mode = RGB_MATRIX_STARTUP_MODE,
  121. .hsv = {
  122. .h = RGB_MATRIX_STARTUP_HUE,
  123. .s = RGB_MATRIX_STARTUP_SAT,
  124. .v = RGB_MATRIX_STARTUP_VAL,
  125. },
  126. .speed = RGB_MATRIX_STARTUP_SPD,
  127. .flags = LED_FLAG_KEYLIGHT,
  128. },
  129. // Layer 1
  130. {
  131. .enable = 1,
  132. .mode = RGB_MATRIX_CUSTOM_active_keys,
  133. .hsv = {
  134. .h = RGB_MATRIX_STARTUP_HUE,
  135. .s = RGB_MATRIX_STARTUP_SAT,
  136. .v = RGB_MATRIX_STARTUP_VAL,
  137. },
  138. .speed = RGB_MATRIX_STARTUP_SPD,
  139. .flags = LED_FLAG_KEYLIGHT,
  140. },
  141. // Layer 2
  142. {
  143. .enable = 1,
  144. .mode = RGB_MATRIX_CUSTOM_active_keys,
  145. .hsv = {
  146. .h = RGB_MATRIX_STARTUP_HUE,
  147. .s = RGB_MATRIX_STARTUP_SAT,
  148. .v = RGB_MATRIX_STARTUP_VAL,
  149. },
  150. .speed = RGB_MATRIX_STARTUP_SPD,
  151. .flags = LED_FLAG_KEYLIGHT,
  152. },
  153. // Layer 3
  154. {
  155. .enable = 1,
  156. .mode = RGB_MATRIX_CUSTOM_active_keys,
  157. .hsv = {
  158. .h = RGB_MATRIX_STARTUP_HUE,
  159. .s = RGB_MATRIX_STARTUP_SAT,
  160. .v = RGB_MATRIX_STARTUP_VAL,
  161. },
  162. .speed = RGB_MATRIX_STARTUP_SPD,
  163. .flags = LED_FLAG_KEYLIGHT,
  164. },
  165. };
  166. // clang-format on
  167. // Read or write EEPROM data with checks for being inside System76 EC region.
  168. static bool system76_ec_eeprom_op(void *buf, uint16_t size, uint16_t offset, bool write) {
  169. uint16_t addr = SYSTEM76_EC_EEPROM_ADDR + offset;
  170. uint16_t end = addr + size;
  171. // Check for overflow and zero size
  172. if ((end > addr) && (addr >= SYSTEM76_EC_EEPROM_ADDR) && (end <= (SYSTEM76_EC_EEPROM_ADDR + SYSTEM76_EC_EEPROM_SIZE))) {
  173. if (write) {
  174. eeprom_update_block((const void *)buf, (void *)addr, size);
  175. } else {
  176. eeprom_read_block((void *)buf, (const void *)addr, size);
  177. }
  178. return true;
  179. } else {
  180. return false;
  181. }
  182. }
  183. // Read or write EEPROM RGB parameters.
  184. void system76_ec_rgb_eeprom(bool write) {
  185. uint16_t layer_rgb_size = sizeof(layer_rgb);
  186. system76_ec_eeprom_op((void *)layer_rgb, layer_rgb_size, 0, write);
  187. system76_ec_eeprom_op((void *)raw_rgb_data, sizeof(raw_rgb_data), layer_rgb_size, write);
  188. }
  189. // Update RGB parameters on layer change.
  190. void system76_ec_rgb_layer(layer_state_t layer_state) {
  191. if (!bootloader_unlocked) {
  192. uint8_t layer = get_highest_layer(layer_state);
  193. if (layer < DYNAMIC_KEYMAP_LAYER_COUNT) {
  194. rgb_matrix_config = layer_rgb[layer];
  195. }
  196. }
  197. }
  198. #endif // RGB_MATRIX_CUSTOM_KB
  199. void raw_hid_receive(uint8_t *data, uint8_t length) {
  200. // Error response by default, set to success by commands
  201. data[1] = 1;
  202. switch (data[0]) {
  203. case CMD_PROBE:
  204. // Signature
  205. data[2] = 0x76;
  206. data[3] = 0xEC;
  207. // Version
  208. data[4] = 0x01;
  209. data[1] = 0;
  210. break;
  211. case CMD_BOARD:
  212. strncpy((char *)&data[2], QMK_KEYBOARD, length - 2);
  213. data[1] = 0;
  214. break;
  215. case CMD_VERSION:
  216. strncpy((char *)&data[2], QMK_VERSION, length - 2);
  217. data[1] = 0;
  218. break;
  219. case CMD_RESET:
  220. if (bootloader_unlocked) {
  221. data[1] = 0;
  222. bootloader_reset = true;
  223. }
  224. break;
  225. case CMD_KEYMAP_GET: {
  226. uint16_t value = 0;
  227. if (keymap_get(data[2], data[3], data[4], &value)) {
  228. data[5] = (uint8_t)value;
  229. data[6] = (uint8_t)(value >> 8);
  230. data[1] = 0;
  231. }
  232. } break;
  233. case CMD_KEYMAP_SET: {
  234. uint16_t value = ((uint16_t)data[5]) | (((uint16_t)data[6]) << 8);
  235. if (keymap_set(data[2], data[3], data[4], value)) {
  236. data[1] = 0;
  237. }
  238. } break;
  239. #ifdef RGB_MATRIX_CUSTOM_KB
  240. case CMD_LED_GET_VALUE:
  241. if (!bootloader_unlocked) {
  242. uint8_t index = data[2];
  243. for (uint8_t layer = 0; layer < DYNAMIC_KEYMAP_LAYER_COUNT; layer++) {
  244. if (index == (0xF0 | layer)) {
  245. data[3] = layer_rgb[layer].hsv.v;
  246. data[4] = RGB_MATRIX_MAXIMUM_BRIGHTNESS;
  247. data[1] = 0;
  248. break;
  249. }
  250. }
  251. }
  252. break;
  253. case CMD_LED_SET_VALUE:
  254. if (!bootloader_unlocked) {
  255. uint8_t index = data[2];
  256. uint8_t value = data[3];
  257. if (value >= RGB_MATRIX_MAXIMUM_BRIGHTNESS) {
  258. value = RGB_MATRIX_MAXIMUM_BRIGHTNESS;
  259. }
  260. for (uint8_t layer = 0; layer < DYNAMIC_KEYMAP_LAYER_COUNT; layer++) {
  261. if (index == (0xF0 | layer)) {
  262. layer_rgb[layer].hsv.v = value;
  263. data[1] = 0;
  264. system76_ec_rgb_layer(layer_state);
  265. break;
  266. }
  267. }
  268. }
  269. break;
  270. case CMD_LED_GET_COLOR:
  271. if (!bootloader_unlocked) {
  272. uint8_t index = data[2];
  273. if (index < DRIVER_LED_TOTAL) {
  274. data[3] = raw_rgb_data[index].r;
  275. data[4] = raw_rgb_data[index].g;
  276. data[5] = raw_rgb_data[index].b;
  277. data[1] = 0;
  278. } else {
  279. for (uint8_t layer = 0; layer < DYNAMIC_KEYMAP_LAYER_COUNT; layer++) {
  280. if (index == (0xF0 | layer)) {
  281. data[3] = layer_rgb[layer].hsv.h;
  282. data[4] = layer_rgb[layer].hsv.s;
  283. data[5] = 0;
  284. data[1] = 0;
  285. break;
  286. }
  287. }
  288. }
  289. }
  290. break;
  291. case CMD_LED_SET_COLOR:
  292. if (!bootloader_unlocked) {
  293. uint8_t index = data[2];
  294. RGB rgb = {
  295. .r = data[3],
  296. .g = data[4],
  297. .b = data[5],
  298. };
  299. if (index < DRIVER_LED_TOTAL) {
  300. raw_rgb_data[index] = rgb;
  301. data[1] = 0;
  302. } else {
  303. for (uint8_t layer = 0; layer < DYNAMIC_KEYMAP_LAYER_COUNT; layer++) {
  304. if (index == (0xF0 | layer)) {
  305. layer_rgb[layer].hsv.h = rgb.r;
  306. layer_rgb[layer].hsv.s = rgb.g;
  307. // Ignore rgb.b
  308. data[1] = 0;
  309. system76_ec_rgb_layer(layer_state);
  310. break;
  311. }
  312. }
  313. }
  314. }
  315. break;
  316. case CMD_LED_GET_MODE:
  317. if (!bootloader_unlocked) {
  318. uint8_t layer = data[2];
  319. if (layer < DYNAMIC_KEYMAP_LAYER_COUNT) {
  320. enum rgb_matrix_effects mode = layer_rgb[layer].mode;
  321. for (uint8_t i = 0; i < MODE_LAST; i++) {
  322. if (mode_map[i] == mode) {
  323. data[3] = i;
  324. data[4] = layer_rgb[layer].speed;
  325. data[1] = 0;
  326. break;
  327. }
  328. }
  329. }
  330. }
  331. break;
  332. case CMD_LED_SET_MODE:
  333. if (!bootloader_unlocked) {
  334. uint8_t layer = data[2];
  335. uint8_t mode = data[3];
  336. uint8_t speed = data[4];
  337. if (layer < DYNAMIC_KEYMAP_LAYER_COUNT && mode < MODE_LAST) {
  338. layer_rgb[layer].mode = mode_map[mode];
  339. layer_rgb[layer].speed = speed;
  340. data[1] = 0;
  341. system76_ec_rgb_layer(layer_state);
  342. }
  343. }
  344. break;
  345. case CMD_LED_SAVE:
  346. if (!bootloader_unlocked) {
  347. system76_ec_rgb_eeprom(true);
  348. data[1] = 0;
  349. }
  350. break;
  351. #endif // RGB_MATRIX_CUSTOM_KB
  352. case CMD_MATRIX_GET: {
  353. // TODO: Improve performance?
  354. data[2] = matrix_rows();
  355. data[3] = matrix_cols();
  356. uint8_t byte = 4;
  357. uint8_t bit = 0;
  358. for (uint8_t row = 0; row < matrix_rows(); row++) {
  359. for (uint8_t col = 0; col < matrix_cols(); col++) {
  360. if (byte < length) {
  361. if (matrix_is_on(row, col)) {
  362. data[byte] |= (1 << bit);
  363. } else {
  364. data[byte] &= ~(1 << bit);
  365. }
  366. }
  367. bit++;
  368. if (bit >= 8) {
  369. byte++;
  370. bit = 0;
  371. }
  372. }
  373. }
  374. data[1] = 0;
  375. } break;
  376. case CMD_SET_NO_INPUT: {
  377. clear_keyboard();
  378. input_disabled = data[2] != 0;
  379. data[1] = 0;
  380. } break;
  381. }
  382. raw_hid_send(data, length);
  383. if (bootloader_reset) {
  384. // Give host time to read response
  385. wait_ms(100);
  386. // Jump to the bootloader
  387. bootloader_jump();
  388. }
  389. }