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