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