qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

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 }