logo

qmk_firmware

custom branch of QMK firmware git clone https://anongit.hacktivis.me/git/qmk_firmware.git

system76_ec.c (13876B)


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