aboutsummaryrefslogtreecommitdiff
path: root/keyboards/system76
diff options
context:
space:
mode:
authorjecassis <jecassis@users.noreply.github.com>2022-01-11 01:39:10 +0000
committerGitHub <noreply@github.com>2022-01-10 17:39:10 -0800
commit8920db2b57b616e162e2d3162577db6f021e7658 (patch)
tree3fc960146f72f75231b70fc0a4c71c590b61355d /keyboards/system76
parentda1a01b811a382ac92b44558dc4f6eec9314571f (diff)
downloadqmk_firmware-8920db2b57b616e162e2d3162577db6f021e7658.tar.gz
qmk_firmware-8920db2b57b616e162e2d3162577db6f021e7658.zip
[Keyboard] Add system76/launch_1 keyboard (#15395)
* WIP: virgo keyboard * Finish layout * Enable debugging and format * Debug keypresses * Add function layer * Fix whitespace * Fix some more whitespace * Add Jeremy's map * Add left split ortho 2U board * Enabled extrakeys for volume & media control * More work on split ortho 2U... ...but still not complete * Finish default layout * Fix many issues by renaming the keyboard * Add right half as a keyboard * Update config for right side * WIP: Add split ortho 2U board * WIP: Correct rules & config * More work on split ortho * More work on split ortho 2u * Nearing completion on split ortho * Remove left and right separate keyboards. Split ortho 2U is complete and they are not needed. * Add uglydense keyboard * Rename directory for uglydense * Swap right Fn and right ctrl keys * Add jeremy's layout * Add ian layout * Add reset key, which is very useful for flashing. * Add Levi's layout * Update Levi's layout * Fix Levi's Layout * Fix Levi's layout again * Add a README with some basic information * Add keymap customization info to uglydense readme * Make the readme make a little more sense. * Make John a layout with left fn and left super swapped * Update John's layout * Add Carl's layout * Add Sean's layout * Add reset keys to all layouts * Swap LALT & LGUI on default layout * shpurk keyboard: initial commit * Add nathaniel & shpurk layouts * Update instructions to include necesarry dependencies * Add Lrrr keyboard, ruler of Omicron Persei 8 * Update README for Lrrr * Update Lrrr it uses Caterina bootloader Also B1 wasn't working for Row 6, so I changed that to F6 * Swap RCTL & RALT * Un-swap RCTL and RALT, making RCTL closer to right thumb * Add printscreen to my layout * Rename lrrr to Launch, enbiggen L-Shift to 2U * Add layout files for Launch * Rename launch to launch_1 * Add levi layout for ortho_split_2u * Update carl keymap * Add launch testboard * Implement keyboard keycode reading using raw hid * Enable dynamic keymap * Add config support to launch_1 * Implement probe command, make logical key names match configurator * Update logical key names again * Add layout generator for keyboard configurator * Add board name and version * Add board name and version to test board * Fix issues with compiling board and version commands * Rename uglydense to launch_alpha_1 and launch_1 to launch_alpha_2 * Generate layouts for other launch prototypes * Fix launch_alpha_1 logical names * Add launch_beta_1 * Fix building production hex file with atmel-dfu bootloader * Limit backlight brightness * USB mux handling * Allow repeat start * Do USB MUX init before bootmagic * Fixes for mux init * Fix register write size for programmable function control * Ensure bit shifts are correct * Improve documentation * Fix when i2c read ack condition happens * Fix extra start in i2c_set * Add ISP instructions * Add fuse information * Refactor * Add RGB matrix support * Fix RGB matrix * Update Jeremy layout * Enable audio controls * Update Jeremy layout * Ensure that n-key rollover is used * Port changes to other launch boards * Configuration values for starting HSV and speed (#7740) * Define default HSV and speed for RGB matrix. * Documentation for configuration values RGB_MATRIX_STARTUP_HUE, RGB_MATRIX_STARTUP_SAT and RGB_MATRIX_STARTUP_VAL. * Document RGB_MATRIX_STARTUP_SPD. * Preserve the ordering. * Set default RGB mode, hue, and saturation * Reduce AVR clock to 8MHz * Update launch_beta_1 with new USB ID * Update default LED mode * Set default hue * Disable RGB while suspended * Add led value and color commands * Add max value to CMD_LED_GET_VALUE * Do not save custom mode to eeprom * Add reset to bootloader command for Launch keyboard * Rename launch_beta_1 to launch_1 * Enable LTO when compiling for launch_1 * Allow setting individual LED's * Convert tabs to spaces * Unlock on RESET keypress: - Display unlock pattern - Disable LED get/set functions - Enable reset to bootloader function * Reduce brightness of rainbow backdrop in unlock pattern * Add hid commands for setting led matrix mode This changes the color setting to not change the mode, and set the hue and saturation for QMK effects. * Fix `CMD_LED_GET_MODE` * Add Levi's Launch layout * Fix layer mistake in Levi's Launch layout * Add matrix command * Define default RGB matrix speed * Add active_keys effect * Move definition of RGB modes inside ifdef testing for custom RGB modes * RGB parameters per layer * fix: Call `system76_ec_rgb_layer` after setting mode * Include layer 3 and 4 in default layout for launch_1 I added support for layer 3 and 4 to the Configurator, but it seems to load bogus values. `dynamic_keymap_reset()` has a comment saying: ``` // Reset the keymaps in EEPROM to what is in flash. // All keyboards using dynamic keymaps should define a layout // for the same number of layers as DYNAMIC_KEYMAP_LAYER_COUNT ``` Other keyboards seem to have default layouts that only list the first two layers while setting `DYNAMIC_KEYMAP_LAYER_COUNT` to 4, but whatever. This appears to make the Configurator behave as expected with layer 3 and 4. * Use EEPROM to store RGB parameters * Add layer 2 and 3 to other keymaps * Add LED_SAVE command * Use eeprom_update_block to improve performance * Revert "Configuration values for starting HSV and speed (#7740)" This reverts commit de1f60fd370b4769336b8a707ee12657aee46412. * Update launch_1 rules.mk for changes in Qmk * WIP keycodes matching EC behavior * Modify default layout to match design * Apply updates to jeremy layout * Improvements to RGB keycodes * system76_ec: Add mode to disable layer backlight * launch_1: Use `KC_NO` instead of `KC_TRNS` for default layout * Revert "launch_1: Use `KC_NO` instead of `KC_TRNS` for default layout" This reverts commit f71c5e7ac3cecbbb1a1f8934db1f329407fef041. * Fix building bootloader * Workaround for upstream orientation * Custom USB IDs for USB hubs, disable USB hub feature controller * Set USB mux orientation in a loop for one second * Set mux orientation 100 times with 10 ms delay * Update Jeremy's keymap * Update Levi's Launch keymap * Update flashing instructions and rewrite layout design instructions * Update README.md * Add a system76_ec command to disable input events For testing purposes. * Enable system76/launch_1 keyboard to work with QMK Firmware 0.15.3 - Migrate system76/launch_1 from 0.7.103: - Explicitly enable used RGB matrix effects - Initialize flags field of `rgb_config_t` union/struct - Account for header and source file location changes - Update AVR platform makefile with Atmel DFU bootloader option - Update ATmega32U4 bootloader to latest from Microchip - Format C sources with ClangFormat - Format Markdown text with Prettier * Remove System76 pre-release or test keyboards and keymaps * Add licensing and replace guards in headers for system76/launch_1 * Remove options impact for system76/launch_1 * Revert AVR platform changes for `atmel-dfu` bootloader * Update system76/launch_1 README * Add system76/launch_1 information JSON file * Replace `util/delay.h` timing abstractions in system76/launch_1 * Use I2C QMK abstractions in system76/launch_1 * Fully revert AVR platform changes for `atmel-dfu` bootloader * Move `layouts.sh` into `keyboards/system76` * Implement GitHub PR suggestions for system76/launch_1 * Make additional system76/launch_1 updates * Implement minor system76/launch_1 change requests * Add custom version of Bootmagic Lite and document fuse values for system76/launch_1 * Remove the RESET HID command from system76/launch_1 * Reorder `process_record_user` in system76/launch_1 * Add `post_rules.mk` to system76/launch_1 * Fix overlapping key in sytem76/launch_1
Diffstat (limited to 'keyboards/system76')
-rw-r--r--keyboards/system76/launch_1/config.h121
-rw-r--r--keyboards/system76/launch_1/info.json94
-rw-r--r--keyboards/system76/launch_1/keymaps/default/keymap.c104
-rw-r--r--keyboards/system76/launch_1/launch_1.c240
-rw-r--r--keyboards/system76/launch_1/launch_1.h38
-rw-r--r--keyboards/system76/launch_1/post_rules.mk12
-rw-r--r--keyboards/system76/launch_1/readme.md62
-rw-r--r--keyboards/system76/launch_1/rgb_matrix_kb.inc157
-rw-r--r--keyboards/system76/launch_1/rules.mk33
-rw-r--r--keyboards/system76/launch_1/usb_mux.c478
-rw-r--r--keyboards/system76/launch_1/usb_mux.h21
-rwxr-xr-xkeyboards/system76/layouts.sh75
-rw-r--r--keyboards/system76/readme.md5
-rw-r--r--keyboards/system76/system76_ec.c416
14 files changed, 1856 insertions, 0 deletions
diff --git a/keyboards/system76/launch_1/config.h b/keyboards/system76/launch_1/config.h
new file mode 100644
index 000000000..19752d58d
--- /dev/null
+++ b/keyboards/system76/launch_1/config.h
@@ -0,0 +1,121 @@
1/*
2 * Copyright (C) 2021 System76
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#pragma once
19
20#include "config_common.h"
21
22// USB device descriptor parameter
23#define VENDOR_ID 0x3384
24#define PRODUCT_ID 0x0001
25#define DEVICE_VER 0x0001
26#define MANUFACTURER System76
27#define PRODUCT Launch Configurable Keyboard (launch_1)
28
29// Key matrix size
30#define MATRIX_ROWS 6
31#define MATRIX_COLS 14
32
33/*
34 * Key matrix pins
35 * ROWS: AVR pins used for rows, top to bottom
36 * COLS: AVR pins used for columns, left to right
37 */
38#define MATRIX_ROW_PINS { F0, F1, F4, F5, F6, F7 }
39#define MATRIX_COL_PINS { D7, C7, C6, B6, B5, B4, D6, D4, E6, D5, D3, D2, B7, B0 }
40#define UNUSED_PINS
41
42/*
43 * Diode Direction
44 * COL2ROW = COL => Anode (+), ROW => Cathode (-)
45 * ROW2COL = ROW => Anode (+), COL => Cathode (-)
46 */
47#define DIODE_DIRECTION COL2ROW
48
49// Set 0 if debouncing isn't needed
50#define DEBOUNCE 5
51
52#ifdef RGB_MATRIX_ENABLE
53# define RGB_DI_PIN E2
54# define DRIVER_LED_TOTAL 84
55# define RGB_MATRIX_KEYPRESSES // Reacts to keypresses
56// # define RGB_MATRIX_KEYRELEASES // Reacts to keyreleases (instead of keypresses)
57// # define RGB_MATRIX_FRAMEBUFFER_EFFECTS // Enables framebuffer effects
58# define RGB_DISABLE_TIMEOUT 0 // Number of milliseconds to wait until RGB automatically turns off
59# define RGB_DISABLE_AFTER_TIMEOUT 0 // OBSOLETE: Number of ticks to wait until disabling effects
60# define RGB_DISABLE_WHEN_USB_SUSPENDED // Turns off effects when suspended
61// Limit brightness to support USB-A at 0.5 A
62// TODO: Do this dynamically based on power source
63# define RGB_MATRIX_MAXIMUM_BRIGHTNESS 176 // Limits maximum brightness of LEDs to 176 out of 255. If not defined, maximum brightness is set to 255
64# define RGB_MATRIX_STARTUP_MODE RGB_MATRIX_RAINBOW_MOVING_CHEVRON // Sets the default mode, if none has been set
65# define RGB_MATRIX_STARTUP_HUE 142 // Sets the default hue value, if none has been set
66# define RGB_MATRIX_STARTUP_SAT 255 // Sets the default saturation value, if none has been set
67# define RGB_MATRIX_STARTUP_VAL RGB_MATRIX_MAXIMUM_BRIGHTNESS // Sets the default brightness value, if none has been set
68# define RGB_MATRIX_STARTUP_SPD 127 // Sets the default animation speed, if none has been set
69# define RGB_MATRIX_DISABLE_KEYCODES // Disables control of rgb matrix by keycodes (must use code functions to control the feature)
70
71# define ENABLE_RGB_MATRIX_CYCLE_ALL
72# define ENABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT
73# define ENABLE_RGB_MATRIX_CYCLE_UP_DOWN
74# define ENABLE_RGB_MATRIX_CYCLE_OUT_IN
75# define ENABLE_RGB_MATRIX_CYCLE_OUT_IN_DUAL
76# define ENABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
77# define ENABLE_RGB_MATRIX_CYCLE_PINWHEEL
78# define ENABLE_RGB_MATRIX_CYCLE_SPIRAL
79# define ENABLE_RGB_MATRIX_RAINDROPS
80# define ENABLE_RGB_MATRIX_SPLASH
81# define ENABLE_RGB_MATRIX_MULTISPLASH
82#endif // RGB_MATRIX_ENABLE
83
84// Mechanical locking support; use KC_LCAP, KC_LNUM, or KC_LSCR instead in keymap
85#define LOCKING_SUPPORT_ENABLE
86
87// Locking resynchronize hack
88#define LOCKING_RESYNC_ENABLE
89
90// I2C {
91#define F_SCL 100000UL // Run I2C bus at 100 kHz
92#define I2C_START_RETRY_COUNT 20
93#define I2C_TIMEOUT 100 // milliseconds
94// } I2C
95
96// EEPROM {
97#define EEPROM_SIZE 1024
98// TODO: Refactor with new user EEPROM code (coming soon)
99#define EEPROM_MAGIC 0x76EC
100#define EEPROM_MAGIC_ADDR 64
101// Bump this every time we change what we store
102// This will automatically reset the EEPROM with defaults
103// and avoid loading invalid data from the EEPROM
104#define EEPROM_VERSION 0x02
105#define EEPROM_VERSION_ADDR (EEPROM_MAGIC_ADDR + 2)
106// } EEPROM
107
108// Dynamic keymap {
109#define DYNAMIC_KEYMAP_LAYER_COUNT 4
110#define DYNAMIC_KEYMAP_MACRO_COUNT 0
111// Dynamic keymap starts after EEPROM version
112#define DYNAMIC_KEYMAP_EEPROM_ADDR (EEPROM_VERSION_ADDR + 1)
113// Dynamic macro starts after dynamic keymaps, it is disabled
114#define DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR (DYNAMIC_KEYMAP_EEPROM_ADDR + (DYNAMIC_KEYMAP_LAYER_COUNT * MATRIX_ROWS * MATRIX_COLS * 2))
115#define DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE 0
116// } Dynamic keymap
117
118// System76 EC {
119#define SYSTEM76_EC_EEPROM_ADDR (DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE)
120#define SYSTEM76_EC_EEPROM_SIZE (EEPROM_SIZE - SYSTEM76_EC_EEPROM_ADDR)
121// } System76 EC
diff --git a/keyboards/system76/launch_1/info.json b/keyboards/system76/launch_1/info.json
new file mode 100644
index 000000000..10d39cc75
--- /dev/null
+++ b/keyboards/system76/launch_1/info.json
@@ -0,0 +1,94 @@
1{
2 "keyboard_name": "System76 Launch Configurable Keyboard (launch_1)",
3 "url": "https://system76.com/accessories/launch",
4 "layouts": {
5 "LAYOUT": {
6 "layout": [
7 { "label": "Esc", "x": 0, "y": 0 },
8 { "label": "F1", "x": 1, "y": 0 },
9 { "label": "F2", "x": 2, "y": 0 },
10 { "label": "F3", "x": 3, "y": 0 },
11 { "label": "F4", "x": 4, "y": 0 },
12 { "label": "F5", "x": 5, "y": 0 },
13 { "label": "F6", "x": 6, "y": 0 },
14 { "label": "F7", "x": 7, "y": 0 },
15 { "label": "F8", "x": 8, "y": 0 },
16 { "label": "F9", "x": 9, "y": 0 },
17 { "label": "F10", "x": 10, "y": 0 },
18 { "label": "F11", "x": 11, "y": 0 },
19 { "label": "F12", "x": 12, "y": 0 },
20 { "label": "Del", "x": 13, "y": 0, "w": 1.5 },
21 { "label": "Home", "x": 14.75, "y": 0 },
22 { "label": "`", "x": 0, "y": 1 },
23 { "label": "1", "x": 1, "y": 1 },
24 { "label": "2", "x": 2, "y": 1 },
25 { "label": "3", "x": 3, "y": 1 },
26 { "label": "4", "x": 4, "y": 1 },
27 { "label": "5", "x": 5, "y": 1 },
28 { "label": "6", "x": 6, "y": 1 },
29 { "label": "7", "x": 7, "y": 1 },
30 { "label": "8", "x": 8, "y": 1 },
31 { "label": "9", "x": 9, "y": 1 },
32 { "label": "0", "x": 10, "y": 1 },
33 { "label": "-", "x": 11, "y": 1 },
34 { "label": "=", "x": 12, "y": 1 },
35 { "label": "Bksp", "x": 13, "y": 1, "w": 1.5 },
36 { "label": "PgUp", "x": 14.75, "y": 1 },
37 { "label": "Tab", "x": 0, "y": 2, "w": 1.5 },
38 { "label": "Q", "x": 1.5, "y": 2 },
39 { "label": "W", "x": 2.5, "y": 2 },
40 { "label": "E", "x": 3.5, "y": 2 },
41 { "label": "R", "x": 4.5, "y": 2 },
42 { "label": "T", "x": 5.5, "y": 2 },
43 { "label": "Y", "x": 6.5, "y": 2 },
44 { "label": "U", "x": 7.5, "y": 2 },
45 { "label": "I", "x": 8.5, "y": 2 },
46 { "label": "O", "x": 9.5, "y": 2 },
47 { "label": "P", "x": 10.5, "y": 2 },
48 { "label": "[", "x": 11.5, "y": 2 },
49 { "label": "]", "x": 12.5, "y": 2 },
50 { "label": "\\", "x": 13.5, "y": 2 },
51 { "label": "PgDn", "x": 14.75, "y": 2 },
52 { "label": "Caps", "x": 0.25, "y": 3, "w": 1.5 },
53 { "label": "A", "x": 1.75, "y": 3 },
54 { "label": "S", "x": 2.75, "y": 3 },
55 { "label": "D", "x": 3.75, "y": 3 },
56 { "label": "F", "x": 4.75, "y": 3 },
57 { "label": "G", "x": 5.75, "y": 3 },
58 { "label": "H", "x": 6.75, "y": 3 },
59 { "label": "J", "x": 7.75, "y": 3 },
60 { "label": "K", "x": 8.75, "y": 3 },
61 { "label": "L", "x": 9.75, "y": 3 },
62 { "label": ";", "x": 10.75, "y": 3 },
63 { "label": "'", "x": 11.75, "y": 3 },
64 { "label": "Enter", "x": 12.75, "y": 3, "w": 1.5 },
65 { "label": "End", "x": 14.75, "y": 3 },
66 { "label": "LShift", "x": 0.25, "y": 4, "w": 2 },
67 { "label": "Z", "x": 2.25, "y": 4 },
68 { "label": "X", "x": 3.25, "y": 4 },
69 { "label": "C", "x": 4.25, "y": 4 },
70 { "label": "V", "x": 5.25, "y": 4 },
71 { "label": "B", "x": 6.25, "y": 4 },
72 { "label": "N", "x": 7.25, "y": 4 },
73 { "label": "M", "x": 8.25, "y": 4 },
74 { "label": ",", "x": 9.25, "y": 4 },
75 { "label": ".", "x": 10.25, "y": 4 },
76 { "label": "/", "x": 11.25, "y": 4 },
77 { "label": "RShift", "x": 12.25, "y": 4, "w": 1.5 },
78 { "label": "Up", "x": 13.75, "y": 4 },
79 { "label": "LCtrl", "x": 0.25, "y": 5, "w": 1.5 },
80 { "label": "LAlt", "x": 1.75, "y": 5 },
81 { "label": "LFn", "x": 2.75, "y": 5 },
82 { "label": "Super", "x": 3.75, "y": 5 },
83 { "label": "Space", "x": 4.75, "y": 5, "w": 2 },
84 { "label": "Space", "x": 6.75, "y": 5, "w": 2 },
85 { "label": "RCtrl", "x": 8.75, "y": 5 },
86 { "label": "RAlt", "x": 9.75, "y": 5 },
87 { "label": "RFn", "x": 10.75, "y": 5, "w": 1.5 },
88 { "label": "Left", "x": 12.75, "y": 5 },
89 { "label": "Down", "x": 13.75, "y": 5 },
90 { "label": "Right", "x": 14.75, "y": 5 }
91 ]
92 }
93 }
94}
diff --git a/keyboards/system76/launch_1/keymaps/default/keymap.c b/keyboards/system76/launch_1/keymaps/default/keymap.c
new file mode 100644
index 000000000..f9f86b658
--- /dev/null
+++ b/keyboards/system76/launch_1/keymaps/default/keymap.c
@@ -0,0 +1,104 @@
1/*
2 * Copyright (C) 2021 System76
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#include QMK_KEYBOARD_H
19
20const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
21
22 /* Layer 0, default layer
23 _________________________________________________________________________________________________________________________________ ________
24| | | | | | | | | | | | | | || |
25| ESC | F1 | F2 | F3 | F4 | F5 | F6 | F7 | F8 | F9 | F10 | F11 | F12 | DELETE || HOME |
26|________|________|________|________|________|________|________|________|________|________|________|________|________|____________||________|
27| ~ | ! | @ | # | $ | % | ^ | & | * | ( | ) | _ | + | || |
28| ` | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | - | = | BACKSPACE || PGUP |
29|________|________|________|________|________|________|________|________|________|________|________|________|________|____________||________|
30| | | | | | | | | | | | [ | ] | | || |
31| TAB | Q | W | E | R | T | Y | U | I | O | P | { | } | \ || PGDN |
32|____________|________|________|________|________|________|________|________|________|________|________|________|________|________||________|
33 | | | | | | | | | | | : | " | | | |
34 | CAPS | A | S | D | F | G | H | J | K | L | ; | ' | ENTER | | END |
35 |____________|________|________|________|________|________|________|________|________|________|________|________|____________|___|________|
36 | | | | | | | | | < | > | ? | | |
37 | SHIFT | Z | X | C | V | B | N | M | , | . | / | SHIFT | UP |
38 |________________|________|________|________|________|________|________|________|________|________|________|____________|________|________
39 | | | | | | | | | | | | | |
40 | CTRL | LALT | FN | LGUI | SPACE | SPACE | RCTRL | RALT | FN | | LEFT | DOWN | RIGHT |
41 |____________|________|_______|________|_________________|_________________|________|________|_____________| |________|________|________|
42 */
43
44 [0] = LAYOUT(
45 KC_ESC, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_DEL, KC_HOME,
46 KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_PGUP,
47 KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_PGDN,
48 KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_END,
49 KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP,
50 KC_LCTL, KC_LALT, MO(1), KC_LGUI, KC_SPC, KC_SPC, KC_RCTL, KC_RALT, MO(1), KC_LEFT, KC_DOWN, KC_RGHT
51 ),
52
53 /* Layer 1, function layer
54 _________________________________________________________________________________________________________________________________ ________
55| | | | | | | | | | | | | | || PLAY/ |
56| RESET | | | | | | | | | | | | | || PAUSE |
57|________|________|________|________|________|________|________|________|________|________|________|________|________|____________||________|
58| | | | | | | | | | | LED | LED | LED | || VOLUME |
59| | | | | | | | | | | TOGGLE | DOWN | UP | || UP |
60|________|________|________|________|________|________|________|________|________|________|________|________|________|____________||________|
61| | | | | | | | | | | | | | || VOLUME |
62|PRINT SCREEN| | | | | | HOME | PGDN | PGUP | END | | | | || DOWN |
63|____________|________|________|________|________|________|________|________|________|________|________|________|________|________||________|
64 | | | | | | | | | | | | | | | |
65 | | | | | | | LEFT | DOWN | UP | RIGHT | | | | | MUTE |
66 |____________|________|________|________|________|________|________|________|________|________|________|________|____________|___|________|
67 | | | | | | | | | | | | | |
68 | | | | | | | | | | | | | PGUP |
69 |________________|________|________|________|________|________|________|________|________|________|________|____________|________|________
70 | | | | | | | | | | | | | |
71 | | | | | | | | | | | HOME | PGDN | END |
72 |____________|________|_______|________|_________________|_________________|________|________|_____________| |________|________|________|
73
74 * `RESET' resets the controller and puts the board into firmware flashing mode.
75 * If this key is hit accidentally, just unplug the board and plug it back in.
76 */
77
78 [1] = LAYOUT(
79 RESET, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_MPLY,
80 KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, RGB_TOG, RGB_VAD, RGB_VAI, KC_TRNS, KC_VOLU,
81 KC_PSCR, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_HOME, KC_PGDN, KC_PGUP, KC_END, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_VOLD,
82 KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT, KC_TRNS, KC_TRNS, KC_TRNS, KC_MUTE,
83 KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_PGUP,
84 KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_HOME, KC_PGDN, KC_END
85 ),
86
87 [2] = LAYOUT(
88 KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
89 KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
90 KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
91 KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
92 KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
93 KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS
94 ),
95
96 [3] = LAYOUT(
97 KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
98 KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
99 KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
100 KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
101 KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
102 KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS
103 ),
104};
diff --git a/keyboards/system76/launch_1/launch_1.c b/keyboards/system76/launch_1/launch_1.c
new file mode 100644
index 000000000..0250b9d9c
--- /dev/null
+++ b/keyboards/system76/launch_1/launch_1.c
@@ -0,0 +1,240 @@
1/*
2 * Copyright (C) 2021 System76
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#include "launch_1.h"
19
20#include "usb_mux.h"
21
22// clang-format off
23#ifdef RGB_MATRIX_ENABLE
24// LEDs by index
25// 0 1 2 3 4 5 6 7 8 9
26// 00 LM4 LL4 LK4 LJ4 LI4 LH4 LG4 LF4 LE4 LD4
27// 10 LC4 LB4 LA4 LA5 LB5 LC5 LD5 LE5 LG5 LH5
28// 20 LI5 LJ5 LK5 LL5 LM5 LO3 LM3 LL3 LK3 LJ3
29// 30 LI3 LH3 LG3 LF3 LE3 LD3 LC3 LB3 LA3 LA2
30// 40 LB2 LC2 LD2 LE2 LF2 LG2 LH2 LI2 LJ2 LK2
31// 50 LL2 LM2 LN2 LO2 LO1 LN1 LM1 LL1 LK1 LJ1
32// 60 LI1 LH1 LG1 LF1 LE1 LD1 LC1 LB1 LA1 LA0
33// 70 LB0 LC0 LD0 LE0 LF0 LG0 LH0 LI0 LJ0 LK0
34// 80 LL0 LM0 LN0 LO0
35led_config_t g_led_config = { LAYOUT(
36 // Key matrix to LED index
37 /* A B C D E F G H I J K L M N O */
38/* 0 */ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
39/* 1 */ 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54,
40/* 2 */ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
41/* 3 */ 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25,
42/* 4 */ 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
43/* 5 */ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24
44), {
45 // LED index to physical position (see leds.sh in `launch' repo)
46/* 00 */ {209, 51}, {190, 51}, {171, 51}, {156, 51}, {140, 51}, {125, 51}, {110, 51}, {95, 51}, {80, 51}, {65, 51},
47/* 10 */ {49, 51}, {34, 51}, {11, 51}, {8, 64}, {27, 64}, {42, 64}, {57, 64}, {80, 64}, {110, 64}, {133, 64},
48/* 20 */ {148, 64}, {167, 64}, {194, 64}, {209, 64}, {224, 64}, {224, 38}, {197, 38}, {178, 38}, {163, 38}, {148, 38},
49/* 30 */ {133, 38}, {118, 38}, {103, 38}, {87, 38}, {72, 38}, {57, 38}, {42, 38}, {27, 38}, {8, 38}, {4, 26},
50/* 40 */ {23, 26}, {38, 26}, {53, 26}, {68, 26}, {84, 26}, {99, 26}, {114, 26}, {129, 26}, {144, 26}, {159, 26},
51/* 50 */ {175, 26}, {190, 26}, {205, 26}, {224, 26}, {224, 13}, {201, 13}, {182, 13}, {167, 13}, {152, 13}, {137, 13},
52/* 60 */ {121, 13}, {106, 13}, {91, 13}, {76, 13}, {61, 13}, {46, 13}, {30, 13}, {15, 13}, {0, 13}, {0, 0},
53/* 70 */ {15, 0}, {30, 0}, {46, 0}, {61, 0}, {76, 0}, {91, 0}, {106, 0}, {121, 0}, {137, 0}, {152, 0},
54/* 80 */ {167, 0}, {182, 0}, {201, 0}, {224, 0}
55}, {
56 // LED index to flags (set all to LED_FLAG_KEYLIGHT)
57 /* 0 1 2 3 4 5 6 7 8 9 */
58/* 00 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
59/* 10 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
60/* 20 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
61/* 30 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
62/* 40 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
63/* 50 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
64/* 60 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
65/* 70 */ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
66/* 80 */ 4, 4, 4, 4
67} };
68#endif // RGB_MATRIX_ENABLE
69
70bool eeprom_is_valid(void) {
71 return (
72 eeprom_read_word(((void *)EEPROM_MAGIC_ADDR)) == EEPROM_MAGIC &&
73 eeprom_read_byte(((void *)EEPROM_VERSION_ADDR)) == EEPROM_VERSION
74 );
75}
76// clang-format on
77
78void eeprom_set_valid(bool valid) {
79 eeprom_update_word(((void *)EEPROM_MAGIC_ADDR), valid ? EEPROM_MAGIC : 0xFFFF);
80 eeprom_update_byte(((void *)EEPROM_VERSION_ADDR), valid ? EEPROM_VERSION : 0xFF);
81}
82
83void bootmagic_lite_reset_eeprom(void) {
84 // Set the keyboard-specific EEPROM state as invalid
85 eeprom_set_valid(false);
86 // Set the TMK/QMK EEPROM state as invalid
87 eeconfig_disable();
88}
89
90// The lite version of TMK's bootmagic based on Wilba.
91// 100% less potential for accidentally making the keyboard do stupid things.
92void bootmagic_lite(void) {
93 // Perform multiple scans because debouncing can't be turned off.
94 matrix_scan();
95#if defined(DEBOUNCE) && DEBOUNCE > 0
96 wait_ms(DEBOUNCE * 2);
97#else
98 wait_ms(30);
99#endif
100 matrix_scan();
101
102 // If the configured key (commonly Esc) is held down on power up,
103 // reset the EEPROM valid state and jump to bootloader.
104 uint8_t row = 0; // BOOTMAGIC_LITE_ROW;
105 uint8_t col = 0; // BOOTMAGIC_LITE_COLUMN;
106
107 if (matrix_get_row(row) & (1 << col)) {
108 bootmagic_lite_reset_eeprom();
109
110 // Jump to bootloader.
111 bootloader_jump();
112 }
113}
114
115void system76_ec_rgb_eeprom(bool write);
116void system76_ec_rgb_layer(layer_state_t layer_state);
117void system76_ec_unlock(void);
118bool system76_ec_is_unlocked(void);
119
120rgb_config_t layer_rgb[DYNAMIC_KEYMAP_LAYER_COUNT];
121
122void matrix_init_kb(void) {
123 usb_mux_init();
124
125 bootmagic_lite();
126 if (!eeprom_is_valid()) {
127 dynamic_keymap_reset();
128 dynamic_keymap_macro_reset();
129 system76_ec_rgb_eeprom(true);
130 eeprom_set_valid(true);
131 } else {
132 system76_ec_rgb_eeprom(false);
133 }
134
135 system76_ec_rgb_layer(layer_state);
136}
137
138void matrix_scan_kb(void) {
139 usb_mux_event();
140
141 matrix_scan_user();
142}
143
144#define LEVEL(value) (uint8_t)(((uint16_t)value) * ((uint16_t)RGB_MATRIX_MAXIMUM_BRIGHTNESS) / ((uint16_t)255))
145
146// clang-format off
147static const uint8_t levels[] = {
148 LEVEL(48),
149 LEVEL(72),
150 LEVEL(96),
151 LEVEL(144),
152 LEVEL(192),
153 LEVEL(255)
154};
155// clang-format on
156
157static uint8_t toggle_level = RGB_MATRIX_MAXIMUM_BRIGHTNESS;
158extern bool input_disabled;
159
160static void set_value_all_layers(uint8_t value) {
161 if (!system76_ec_is_unlocked()) {
162 for (int8_t layer = 0; layer < DYNAMIC_KEYMAP_LAYER_COUNT; layer++) {
163 layer_rgb[layer].hsv.v = value;
164 }
165 system76_ec_rgb_layer(layer_state);
166 }
167}
168
169bool process_record_kb(uint16_t keycode, keyrecord_t *record) {
170 if (input_disabled) {
171 return false;
172 }
173
174 if (!process_record_user(keycode, record)) {
175 return false;
176 }
177
178 switch (keycode) {
179 case RESET:
180 if (record->event.pressed) {
181 system76_ec_unlock();
182 }
183#ifdef SYSTEM76_EC
184 return false;
185#else
186 return true;
187#endif
188 case RGB_VAD:
189 if (record->event.pressed) {
190 uint8_t level = rgb_matrix_config.hsv.v;
191 for (int i = sizeof(levels) - 1; i >= 0; i--) {
192 if (levels[i] < level) {
193 level = levels[i];
194 break;
195 }
196 }
197 set_value_all_layers(level);
198 }
199 return false;
200 case RGB_VAI:
201 if (record->event.pressed) {
202 uint8_t level = rgb_matrix_config.hsv.v;
203 for (int i = 0; i < sizeof(levels); i++) {
204 if (levels[i] > level) {
205 level = levels[i];
206 break;
207 }
208 }
209 set_value_all_layers(level);
210 }
211 return false;
212 case RGB_TOG:
213 if (record->event.pressed) {
214 uint8_t level = 0;
215 if (rgb_matrix_config.hsv.v == 0) {
216 level = toggle_level;
217 } else {
218 toggle_level = rgb_matrix_config.hsv.v;
219 }
220 set_value_all_layers(level);
221 }
222 return false;
223 }
224
225 return true;
226}
227
228layer_state_t layer_state_set_kb(layer_state_t layer_state) {
229 system76_ec_rgb_layer(layer_state);
230
231 return layer_state_set_user(layer_state);
232}
233
234#ifdef CONSOLE_ENABLE
235void keyboard_post_init_user(void) {
236 debug_enable = true;
237 debug_matrix = false;
238 debug_keyboard = false;
239}
240#endif // CONSOLE_ENABLE
diff --git a/keyboards/system76/launch_1/launch_1.h b/keyboards/system76/launch_1/launch_1.h
new file mode 100644
index 000000000..335b8ecbd
--- /dev/null
+++ b/keyboards/system76/launch_1/launch_1.h
@@ -0,0 +1,38 @@
1/*
2 * Copyright (C) 2021 System76
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#pragma once
19
20#include "quantum.h"
21
22// clang-format off
23#define LAYOUT( \
24 K00, K01, K02, K03, K04, K05, K06, K07, K08, K09, K0A, K0B, K0C, K0D, K0E, \
25 K10, K11, K12, K13, K14, K15, K16, K17, K18, K19, K1A, K1B, K1C, K1D, K1E, \
26 K20, K21, K22, K23, K24, K25, K26, K27, K28, K29, K2A, K2B, K2C, K2D, K2E, \
27 K30, K31, K32, K33, K34, K35, K36, K37, K38, K39, K3A, K3B, K3C, K3D, \
28 K40, K41, K42, K43, K44, K45, K46, K47, K48, K49, K4A, K4B, K4C, \
29 K50, K51, K52, K53, K54, K55, K56, K57, K58, K59, K5A, K5B \
30) { \
31 { K00, K01, K02, K03, K04, K05, K06, K07, K08, K09, K0A, K0B, K0C, K0D }, \
32 { K10, K11, K12, K13, K14, K15, K16, K17, K18, K19, K1A, K1B, K1C, K1D }, \
33 { K20, K21, K22, K23, K24, K25, K26, K27, K28, K29, K2A, K2B, K2C, K2D }, \
34 { K30, K31, K32, K33, K34, K35, K36, K37, K38, K39, K3A, K3B, K3C, K0E }, \
35 { K40, K41, K42, K43, K44, K45, K46, K47, K48, K49, K4A, K4B, K4C, K1E }, \
36 { K50, K51, K52, K53, K54, K3D, K55, K56, K57, K58, K59, K5A, K5B, K2E }, \
37}
38// clang-format on
diff --git a/keyboards/system76/launch_1/post_rules.mk b/keyboards/system76/launch_1/post_rules.mk
new file mode 100644
index 000000000..3751a8b8c
--- /dev/null
+++ b/keyboards/system76/launch_1/post_rules.mk
@@ -0,0 +1,12 @@
1# System76 EC
2# remove the RESET HID command
3VALID_SYSTEM76_EC_TYPES := yes
4SYSTEM76_EC_ENABLE ?= no
5ifneq ($(strip $(SYSTEM76_EC_ENABLE)),no)
6 ifeq ($(filter $(SYSTEM76_EC_ENABLE),$(VALID_SYSTEM76_EC_TYPES)),)
7 $(error SYSTEM76_EC_EN="$(strip $(SYSTEM76_EC_ENABLE))" is not a valid type for the System76 EC option)
8 endif
9 ifneq ($(strip $(SYSTEM76_EC_ENABLE)),no)
10 OPT_DEFS += -DSYSTEM76_EC
11 endif
12endif
diff --git a/keyboards/system76/launch_1/readme.md b/keyboards/system76/launch_1/readme.md
new file mode 100644
index 000000000..1dcdeccc3
--- /dev/null
+++ b/keyboards/system76/launch_1/readme.md
@@ -0,0 +1,62 @@
1# System76 Launch Configurable Keyboard (launch_1)
2
3![System76 Launch Configurable Keyboard](https://images.prismic.io/system76/b71307ac-dae6-4863-b7ca-804cd61c7ef8_launch_overhead.png?auto=compress,format&w=750)
4
5The Launch Configurable Keyboard is engineered to be comfortable, fully customizable, and make your workflow more efficient.
6
7- High-speed USB Hub
8- Works on Linux, Windows and macOS
9- 100% Open Source
10- Made in Colorado
11
12Additional Launch Keyboard resources:
13
14- Keyboard Maintainer: [System76](https://github.com/system76)
15- Hardware Supported: [System76 Launch GitHub Repository](https://github.com/system76/launch)
16- Hardware Availability: [Shop System76](https://system76.com/accessories/launch)
17
18## Building Firmware
19
20To build the firmware using `make` (after setting up the build environment), e.g.:
21
22```bash
23make -r system76/launch_1:default
24```
25
26Equivalently, using the QMK CLI:
27
28```bash
29qmk compile -kb system76/launch_1 -km default
30```
31
32## Flashing Firmware (DFU)
33
34To build and flash the firmware on the keyboard, e.g.:
35
36```bash
37make -r system76/launch_1:default:flash
38```
39
40Equivalently, using the QMK CLI:
41
42```bash
43qmk flash -kb system76/launch_1 -km default
44```
45
46## Flashing Firmware (ISP)
47
48To flash the firmware (and/or bootloader) using ISP refer to the [_ISP Flashing Guide_](https://docs.qmk.fm/#/isp_flashing_guide).
49
50> **Factory fuse values** => Low: `0x5E`, High: `0x99`, Extended: `0xF3`, Lock Bits: `0xFF`
51
52## Environment Setup
53
54See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. If new to QMK, start with the [_Complete Newbs Guide_](https://docs.qmk.fm/#/newbs).
55
56## Bootloader
57
58Enter the bootloader in 3 ways:
59
60- **Bootmagic reset**: Hold down the key at (0,0) in the matrix (Escape) and plug in the keyboard.
61- **Keycode in layout**: Press the key mapped to `RESET` in the second layer (Escape).
62- **Electrical reset**: Briefly short AVR ISP's GND (6) and RST (5) pads on the back of the PCB.
diff --git a/keyboards/system76/launch_1/rgb_matrix_kb.inc b/keyboards/system76/launch_1/rgb_matrix_kb.inc
new file mode 100644
index 000000000..484483e0a
--- /dev/null
+++ b/keyboards/system76/launch_1/rgb_matrix_kb.inc
@@ -0,0 +1,157 @@
1/*
2 * Copyright (C) 2021 System76
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18RGB_MATRIX_EFFECT(active_keys)
19RGB_MATRIX_EFFECT(raw_rgb)
20RGB_MATRIX_EFFECT(unlocked)
21
22#ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
23
24#include "dynamic_keymap.h"
25
26static bool active_keys_initialized = false;
27static uint8_t active_keys_table[DRIVER_LED_TOTAL] = {0};
28
29static void active_keys_initialize(void) {
30 for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
31 for (uint8_t col = 0; col < MATRIX_COLS; col++) {
32 uint8_t led = g_led_config.matrix_co[row][col];
33 if (led < DRIVER_LED_TOTAL && row < 16 && col < 16) {
34 active_keys_table[led] = (row << 4) | col;
35 }
36 }
37 }
38 active_keys_initialized = true;
39}
40
41static bool active_keys(effect_params_t* params) {
42 if (!active_keys_initialized) {
43 active_keys_initialize();
44 }
45
46 RGB_MATRIX_USE_LIMITS(led_min, led_max);
47 uint8_t layer = get_highest_layer(layer_state);
48 RGB rgb = hsv_to_rgb(rgb_matrix_config.hsv);
49
50 for (uint8_t i = led_min; i < led_max; i++) {
51 RGB_MATRIX_TEST_LED_FLAGS();
52
53 uint8_t rowcol = active_keys_table[i];
54 uint8_t row = rowcol >> 4;
55 uint8_t col = rowcol & 0xF;
56 uint16_t keycode = dynamic_keymap_get_keycode(layer, row, col);
57 switch (keycode) {
58 case KC_NO:
59 case KC_TRNS:
60 rgb_matrix_set_color(i, 0, 0, 0);
61 break;
62 default:
63 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
64 break;
65 }
66 }
67
68 return led_max < DRIVER_LED_TOTAL;
69}
70
71RGB raw_rgb_data[DRIVER_LED_TOTAL] = {0};
72
73static uint8_t normalize_component(uint8_t component) {
74 uint16_t x = (uint16_t)component;
75 x *= rgb_matrix_config.hsv.v; // Multiply by current brightness
76 x /= 255; // Divide by maximum brightness
77 return (uint8_t)x;
78}
79
80static RGB normalize_index(uint8_t i) {
81 RGB raw = raw_rgb_data[i];
82 RGB rgb = {
83 .r = normalize_component(raw.r),
84 .g = normalize_component(raw.g),
85 .b = normalize_component(raw.b),
86 };
87 return rgb;
88}
89
90static bool raw_rgb(effect_params_t* params) {
91 RGB_MATRIX_USE_LIMITS(led_min, led_max);
92 for (uint8_t i = led_min; i < led_max; i++) {
93 RGB_MATRIX_TEST_LED_FLAGS();
94 RGB rgb = normalize_index(i);
95 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
96 }
97 return led_max < DRIVER_LED_TOTAL;
98}
99
100static uint8_t unlocked_keys[8][2] = {
101 {2, 7}, // U
102 {4, 6}, // N
103 {3, 9}, // L
104 {2, 9}, // O
105 {4, 3}, // C
106 {3, 8}, // K
107 {2, 3}, // E
108 {3, 3}, // D
109};
110
111static uint8_t unlocked_ticks = 0;
112static uint8_t unlocked_i = 0;
113static uint8_t unlocked_leds_count = 0;
114static uint8_t unlocked_leds[2] = {0, 0};
115
116static bool unlocked(effect_params_t* params) {
117 RGB_MATRIX_USE_LIMITS(led_min, led_max);
118
119 unlocked_ticks++;
120
121 if (params->init) {
122 unlocked_ticks = 0;
123 unlocked_i = 0;
124 }
125
126 if (unlocked_ticks == 0) {
127 if (unlocked_i == 8) {
128 unlocked_leds_count = 0;
129 unlocked_i = 0;
130 } else {
131 unlocked_leds_count = rgb_matrix_map_row_column_to_led(unlocked_keys[unlocked_i][0], unlocked_keys[unlocked_i][1], unlocked_leds);
132 unlocked_i++;
133 }
134 }
135
136 for (uint8_t i = led_min; i < led_max; i++) {
137 RGB_MATRIX_TEST_LED_FLAGS();
138
139 HSV hsv = {
140 .h = i + unlocked_ticks,
141 .s = 0xFF,
142 .v = 0x70,
143 };
144 for (uint8_t j = 0; j < unlocked_leds_count; j++) {
145 if (i == unlocked_leds[j]) {
146 hsv.s = 0;
147 hsv.v = 0xFF;
148 }
149 }
150
151 RGB rgb = hsv_to_rgb(hsv);
152 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
153 }
154 return led_max < DRIVER_LED_TOTAL;
155}
156
157#endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/keyboards/system76/launch_1/rules.mk b/keyboards/system76/launch_1/rules.mk
new file mode 100644
index 000000000..1a0cd63b0
--- /dev/null
+++ b/keyboards/system76/launch_1/rules.mk
@@ -0,0 +1,33 @@
1# MCU name
2MCU = atmega32u4
3
4# CPU frequency divided by two since AVR is at 3.3 V
5F_CPU = 8000000
6
7# External oscillator is 16 MHz
8F_USB = 16000000
9
10# Bootloader selection
11BOOTLOADER = atmel-dfu
12
13# Build options
14# change yes to no to disable
15BOOTMAGIC_ENABLE = no # Bootmagic Lite
16MOUSEKEY_ENABLE = no # Mouse keys
17EXTRAKEY_ENABLE = yes # Audio control and system control
18CONSOLE_ENABLE = no # Console for debug
19COMMAND_ENABLE = no # Commands for debug and configuration
20DYNAMIC_KEYMAP_ENABLE = yes # Reconfigurable keyboard without flashing firmware
21NKRO_ENABLE = yes # USB N-key rollover
22RAW_ENABLE = yes # Raw HID commands (used by Keyboard Configurator)
23BACKLIGHT_ENABLE = no # RGB backlight (conflicts with RGB matrix)
24RGBLIGHT_ENABLE = no # Enable keyboard RGB underglow
25RGB_MATRIX_ENABLE = yes # RGB matrix
26RGB_MATRIX_DRIVER = WS2812
27RGB_MATRIX_CUSTOM_KB = yes # Custom keyboard effects
28AUDIO_ENABLE = no # Audio output
29LTO_ENABLE = yes # Link-time optimization for smaller binary
30
31# Add System76 EC command interface as well as I2C and USB mux drivers
32SRC += system76_ec.c usb_mux.c
33QUANTUM_LIB_SRC += i2c_master.c
diff --git a/keyboards/system76/launch_1/usb_mux.c b/keyboards/system76/launch_1/usb_mux.c
new file mode 100644
index 000000000..6cb04dcdd
--- /dev/null
+++ b/keyboards/system76/launch_1/usb_mux.c
@@ -0,0 +1,478 @@
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 "usb_mux.h"
20
21#include <stdbool.h>
22
23#include "i2c_master.h"
24#include "wait.h"
25
26#define REG_PF1_CTL 0xBF800C04
27#define REG_PIO64_OEN 0xBF800908
28#define REG_PIO64_OUT 0xBF800928
29#define REG_VID 0xBF803000
30#define REG_PRT_SWAP 0xBF8030FA
31#define REG_USB3_HUB_VID 0xBFD2E548
32#define REG_RUNTIME_FLAGS2 0xBFD23408
33#define REG_I2S_FEAT_SEL 0xBFD23412
34
35struct USB7206 {
36 uint8_t addr;
37};
38
39struct USB7206 usb_hub = {.addr = 0x2D};
40
41// Perform USB7206 register access.
42// Returns zero on success or a negative number on error.
43i2c_status_t usb7206_register_access(struct USB7206* self) {
44 uint8_t register_access[3] = {
45 0x99,
46 0x37,
47 0x00,
48 };
49
50 return i2c_transmit(self->addr << 1, register_access, sizeof(register_access), I2C_TIMEOUT);
51}
52
53// Read data from USB7206 register region.
54// Returns number of bytes read on success or a negative number on error.
55i2c_status_t usb7206_read_reg(struct USB7206* self, uint32_t addr, uint8_t* data, int length) {
56 i2c_status_t status;
57
58 uint8_t register_read[9] = {
59 0x00, // Buffer address MSB: always 0
60 0x00, // Buffer address LSB: always 0
61 0x06, // Number of bytes to write to command block buffer area
62 0x01, // Direction: 0 = write, 1 = read
63 (uint8_t)length, // Number of bytes to read from register
64 (uint8_t)(addr >> 24), // Register address byte 3
65 (uint8_t)(addr >> 16), // Register address byte 2
66 (uint8_t)(addr >> 8), // Register address byte 1
67 (uint8_t)(addr >> 0), // Register address byte 0
68 };
69
70 status = i2c_transmit(self->addr << 1, register_read, sizeof(register_read), I2C_TIMEOUT);
71 if (status < 0) {
72 return status;
73 }
74
75 status = usb7206_register_access(self);
76 if (status < 0) {
77 return status;
78 }
79
80 uint8_t read[2] = {
81 0x00, // Buffer address MSB: always 0
82 0x06, // Buffer address LSB: 6 to skip header
83 };
84
85 status = i2c_start((self->addr << 1) | I2C_WRITE, I2C_TIMEOUT);
86 if (status >= 0) {
87 for (uint16_t i = 0; i < sizeof(read); i++) {
88 status = i2c_write(read[i], I2C_TIMEOUT);
89 if (status < 0) {
90 goto error;
91 }
92 }
93 } else {
94 goto error;
95 }
96
97 status = i2c_start((self->addr << 1) | I2C_READ, I2C_TIMEOUT);
98 if (status < 0) {
99 goto error;
100 }
101
102 // Read and ignore buffer length
103 status = i2c_read_ack(I2C_TIMEOUT);
104 if (status < 0) {
105 goto error;
106 }
107
108 for (uint16_t i = 0; i < (length - 1) && status >= 0; i++) {
109 status = i2c_read_ack(I2C_TIMEOUT);
110 if (status >= 0) {
111 data[i] = (uint8_t)status;
112 }
113 }
114
115 if (status >= 0) {
116 status = i2c_read_nack(I2C_TIMEOUT);
117 if (status >= 0) {
118 data[(length - 1)] = (uint8_t)status;
119 }
120 }
121
122error:
123 i2c_stop();
124
125 return (status < 0) ? status : length;
126}
127
128// Read 32-bit value from USB7206 register region.
129// Returns number of bytes read on success or a negative number on error.
130i2c_status_t usb7206_read_reg_32(struct USB7206* self, uint32_t addr, uint32_t* data) {
131 i2c_status_t status;
132
133 // First byte is available length
134 uint8_t bytes[4] = {0, 0, 0, 0};
135
136 status = usb7206_read_reg(self, addr, bytes, sizeof(bytes));
137 if (status < 0) {
138 return status;
139 }
140
141 // Convert from little endian
142 *data = (((uint32_t)bytes[0]) << 0) | (((uint32_t)bytes[1]) << 8) | (((uint32_t)bytes[2]) << 16) | (((uint32_t)bytes[3]) << 24);
143
144 return status;
145}
146
147// Write data to USB7206 register region.
148// Returns number of bytes written on success or a negative number on error.
149i2c_status_t usb7206_write_reg(struct USB7206* self, uint32_t addr, uint8_t* data, int length) {
150 i2c_status_t status;
151
152 uint8_t register_write[9] = {
153 0x00, // Buffer address MSB: always 0
154 0x00, // Buffer address LSB: always 0
155 ((uint8_t)length) + 6, // Number of bytes to write to command block buffer area
156 0x00, // Direction: 0 = write, 1 = read
157 (uint8_t)length, // Number of bytes to write to register
158 (uint8_t)(addr >> 24), // Register address byte 3
159 (uint8_t)(addr >> 16), // Register address byte 2
160 (uint8_t)(addr >> 8), // Register address byte 1
161 (uint8_t)(addr >> 0), // Register address byte 0
162 };
163
164 status = i2c_start((self->addr << 1) | I2C_WRITE, I2C_TIMEOUT);
165 if (status >= 0) {
166 for (uint16_t i = 0; i < sizeof(register_write); i++) {
167 status = i2c_write(register_write[i], I2C_TIMEOUT);
168 if (status < 0) {
169 goto error;
170 }
171 }
172
173 for (uint16_t i = 0; i < length; i++) {
174 status = i2c_write(data[i], I2C_TIMEOUT);
175 if (status < 0) {
176 goto error;
177 }
178 }
179 } else {
180 goto error;
181 }
182
183 i2c_stop();
184
185 status = usb7206_register_access(self);
186 if (status < 0) {
187 goto error;
188 }
189
190error:
191 i2c_stop();
192
193 return (status < 0) ? status : length;
194}
195
196// Write 8-bit value to USB7206 register region.
197// Returns number of bytes written on success or a negative number on error.
198i2c_status_t usb7206_write_reg_8(struct USB7206* self, uint32_t addr, uint8_t data) { return usb7206_write_reg(self, addr, &data, sizeof(data)); }
199
200// Write 32-bit value to USB7206 register region.
201// Returns number of bytes written on success or a negative number on error.
202i2c_status_t usb7206_write_reg_32(struct USB7206* self, uint32_t addr, uint32_t data) {
203 // Convert to little endian
204 uint8_t bytes[4] = {
205 (uint8_t)(data >> 0),
206 (uint8_t)(data >> 8),
207 (uint8_t)(data >> 16),
208 (uint8_t)(data >> 24),
209 };
210
211 return usb7206_write_reg(self, addr, bytes, sizeof(bytes));
212}
213
214// Initialize USB7206.
215// Returns zero on success or a negative number on error.
216int usb7206_init(struct USB7206* self) {
217 i2c_status_t status;
218 uint32_t data;
219
220 // DM and DP are swapped on ports 2 and 3
221 status = usb7206_write_reg_8(self, REG_PRT_SWAP, 0x0C);
222 if (status < 0) {
223 return status;
224 }
225
226 // Disable audio
227 status = usb7206_write_reg_8(self, REG_I2S_FEAT_SEL, 0);
228 if (status < 0) {
229 return status;
230 }
231
232 // Set HFC_DISABLE
233 data = 0;
234 status = usb7206_read_reg_32(self, REG_RUNTIME_FLAGS2, &data);
235 if (status < 0) {
236 return status;
237 }
238 data |= 1;
239 status = usb7206_write_reg_32(self, REG_RUNTIME_FLAGS2, data);
240 if (status < 0) {
241 return status;
242 }
243
244 // Set Vendor ID and Product ID of USB 2 hub
245 status = usb7206_write_reg_32(self, REG_VID, 0x00033384);
246 if (status < 0) {
247 return status;
248 }
249
250 // Set Vendor ID and Product ID of USB 3 hub
251 status = usb7206_write_reg_32(self, REG_USB3_HUB_VID, 0x00043384);
252 if (status < 0) {
253 return status;
254 }
255
256 return 0;
257}
258
259// Attach USB7206.
260// Returns bytes written on success or a negative number on error.
261i2c_status_t usb7206_attach(struct USB7206* self) {
262 uint8_t data[3] = {
263 0xAA,
264 0x56,
265 0x00,
266 };
267
268 return i2c_transmit(self->addr << 1, data, sizeof(data), I2C_TIMEOUT);
269}
270
271struct USB7206_GPIO {
272 struct USB7206* usb7206;
273 uint32_t pf;
274};
275
276struct USB7206_GPIO usb_gpio_sink = {.usb7206 = &usb_hub, .pf = 29}; // UP_SEL = PF29 = GPIO93
277struct USB7206_GPIO usb_gpio_source_left = {.usb7206 = &usb_hub, .pf = 10}; // CL_SEL = PF10 = GPIO74
278struct USB7206_GPIO usb_gpio_source_right = {.usb7206 = &usb_hub, .pf = 25}; // CR_SEL = PF25 = GPIO88
279
280// Set USB7206 GPIO to specified value.
281// Returns zero on success or negative number on error.
282i2c_status_t usb7206_gpio_set(struct USB7206_GPIO* self, bool value) {
283 i2c_status_t status;
284 uint32_t data;
285
286 data = 0;
287 status = usb7206_read_reg_32(self->usb7206, REG_PIO64_OUT, &data);
288 if (status < 0) {
289 return status;
290 }
291
292 if (value) {
293 data |= (((uint32_t)1) << self->pf);
294 } else {
295 data &= ~(((uint32_t)1) << self->pf);
296 }
297 status = usb7206_write_reg_32(self->usb7206, REG_PIO64_OUT, data);
298 if (status < 0) {
299 return status;
300 }
301
302 return 0;
303}
304
305// Initialize USB7206 GPIO.
306// Returns zero on success or a negative number on error.
307i2c_status_t usb7206_gpio_init(struct USB7206_GPIO* self) {
308 i2c_status_t status;
309 uint32_t data;
310
311 // Set programmable function to GPIO
312 status = usb7206_write_reg_8(self->usb7206, REG_PF1_CTL + (self->pf - 1), 0);
313 if (status < 0) {
314 return status;
315 }
316
317 // Set GPIO to false by default
318 usb7206_gpio_set(self, false);
319
320 // Set GPIO to output
321 data = 0;
322 status = usb7206_read_reg_32(self->usb7206, REG_PIO64_OEN, &data);
323 if (status < 0) {
324 return status;
325 }
326
327 data |= (((uint32_t)1) << self->pf);
328 status = usb7206_write_reg_32(self->usb7206, REG_PIO64_OEN, data);
329 if (status < 0) {
330 return status;
331 }
332
333 return 0;
334}
335
336struct PTN5110 {
337 uint8_t addr;
338 uint8_t cc;
339 struct USB7206_GPIO* gpio;
340};
341
342struct PTN5110 usb_sink = {.addr = 0x51, .gpio = &usb_gpio_sink};
343struct PTN5110 usb_source_left = {.addr = 0x52, .gpio = &usb_gpio_source_left};
344struct PTN5110 usb_source_right = {.addr = 0x50, .gpio = &usb_gpio_source_right};
345
346// Initialize PTN5110.
347// Returns zero on success or a negative number on error.
348i2c_status_t ptn5110_init(struct PTN5110* self) {
349 // Set last cc to invalid value, to force update
350 self->cc = 0xFF;
351 // Initialize GPIO
352 return usb7206_gpio_init(self->gpio);
353}
354
355// Read PTN5110 CC_STATUS.
356// Returns zero on success or a negative number on error.
357i2c_status_t ptn5110_get_cc_status(struct PTN5110* self, uint8_t* cc) { return i2c_readReg(self->addr << 1, 0x1D, cc, 1, I2C_TIMEOUT); }
358
359// Set PTN5110 SSMUX orientation.
360// Returns zero on success or a negative number on error.
361i2c_status_t ptn5110_set_ssmux(struct PTN5110* self, bool orientation) { return usb7206_gpio_set(self->gpio, orientation); }
362
363// Write PTN5110 COMMAND.
364// Returns zero on success or negative number on error.
365i2c_status_t ptn5110_command(struct PTN5110* self, uint8_t command) { return i2c_writeReg(self->addr << 1, 0x23, &command, 1, I2C_TIMEOUT); }
366
367// Set orientation of PTN5110 operating as a sink, call this once.
368// Returns zero on success or a negative number on error.
369i2c_status_t ptn5110_sink_set_orientation(struct PTN5110* self) {
370 i2c_status_t status;
371 uint8_t cc;
372
373 status = ptn5110_get_cc_status(self, &cc);
374 if (status < 0) {
375 return status;
376 }
377
378 if ((cc & 0x03) == 0) {
379 status = ptn5110_set_ssmux(self, false);
380 if (status < 0) {
381 return status;
382 }
383 } else {
384 status = ptn5110_set_ssmux(self, true);
385 if (status < 0) {
386 return status;
387 }
388 }
389
390 return 0;
391}
392
393// Update PTN5110 operating as a source, call this repeatedly.
394// Returns zero on success or a negative number on error.
395i2c_status_t ptn5110_source_update(struct PTN5110* self) {
396 i2c_status_t status;
397 uint8_t cc;
398
399 status = ptn5110_get_cc_status(self, &cc);
400 if (status < 0) {
401 return status;
402 }
403
404 if (cc != self->cc) {
405 // WARNING: Setting this here will disable retries
406 self->cc = cc;
407
408 bool connected = false;
409 bool orientation = false;
410 if ((cc & 0x03) == 2) {
411 connected = true;
412 orientation = true;
413 } else if (((cc >> 2) & 0x03) == 2) {
414 connected = true;
415 orientation = false;
416 }
417
418 if (connected) {
419 // Set SS mux orientation
420 status = ptn5110_set_ssmux(self, orientation);
421 if (status < 0) {
422 return status;
423 }
424
425 // Enable source Vbus command
426 status = ptn5110_command(self, 0b01110111);
427 if (status < 0) {
428 return status;
429 }
430 } else {
431 // Disable source Vbus command
432 status = ptn5110_command(self, 0b01100110);
433 if (status < 0) {
434 return status;
435 }
436 }
437 }
438
439 return 0;
440}
441
442void usb_mux_event(void) {
443 // Run this on every 1000th matrix scan
444 static int cycle = 0;
445 if (cycle >= 1000) {
446 cycle = 0;
447 ptn5110_source_update(&usb_source_left);
448 ptn5110_source_update(&usb_source_right);
449 } else {
450 cycle += 1;
451 }
452}
453
454void usb_mux_init(void) {
455 // Run I2C bus at 100 kHz
456 i2c_init();
457
458 // Set up hub
459 usb7206_init(&usb_hub);
460
461 // Set up sink
462 ptn5110_init(&usb_sink);
463 ptn5110_sink_set_orientation(&usb_sink);
464
465 // Set up sources
466 ptn5110_init(&usb_source_left);
467 ptn5110_init(&usb_source_right);
468
469 // Attach hub
470 usb7206_attach(&usb_hub);
471
472 // Ensure orientation is correct after attaching hub
473 // TODO: Find reason why GPIO for sink orientation is reset
474 for (int i = 0; i < 100; i++) {
475 ptn5110_sink_set_orientation(&usb_sink);
476 wait_ms(10);
477 }
478}
diff --git a/keyboards/system76/launch_1/usb_mux.h b/keyboards/system76/launch_1/usb_mux.h
new file mode 100644
index 000000000..26f84de86
--- /dev/null
+++ b/keyboards/system76/launch_1/usb_mux.h
@@ -0,0 +1,21 @@
1/*
2 * Copyright (C) 2021 System76
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#pragma once
19
20void usb_mux_init(void);
21void usb_mux_event(void);
diff --git a/keyboards/system76/layouts.sh b/keyboards/system76/layouts.sh
new file mode 100755
index 000000000..1c9118562
--- /dev/null
+++ b/keyboards/system76/layouts.sh
@@ -0,0 +1,75 @@
1#!/usr/bin/env bash
2#
3# This script produces layout data for the System76 Keyboard Configurator.
4#
5# Copyright (C) 2021 System76
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, version 3.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19set -eEuo pipefail
20
21R=$(git rev-parse --show-toplevel)
22cd "${R}"
23rm -rf .build/layouts
24mkdir -p .build/layouts
25D="$(realpath .build/layouts)"
26
27binary="${D}/keymap"
28source="${binary}.c"
29header="quantum/keycode.h"
30printf "#include <stdio.h>\n" >"$source"
31printf "#include \"%s\"\n\n" "${header}" >>"$source"
32echo "int main(int argc, char **argv) {" >>"$source"
33grep '^ KC_' "$header" |
34 cut -d ' ' -f5 |
35 cut -d ',' -f1 |
36 while read -r keycode; do
37 name=$(echo "${keycode}" | cut -d '_' -f2-)
38 printf " printf(\"%s,0x%%04X\\\n\", $keycode);\n" "${name}" >>"$source"
39 done
40printf "\n return 0;\n}\n" >>"$source"
41gcc -I. "$source" -o "$binary"
42"${binary}" | tee "${D}/keymap.csv"
43
44cd keyboards
45for board in system76/launch_*; do
46 file="$board/$(basename "$board").h"
47 if [ ! -e "$file" ]; then
48 continue
49 fi
50 echo "# ${board}"
51 mkdir -p "${D}/${board}"
52 cp "${D}/keymap.csv" "${D}/${board}"
53 row=0
54 rg \
55 --multiline \
56 --multiline-dotall \
57 --regexp '#define LAYOUT\(.*\) \{.*\}' \
58 "$file" |
59 grep --only-matching '\{.*\}' |
60 sed 's/^{ //' |
61 sed 's/ }$//' |
62 sed 's/, / /g' |
63 while read -r line; do
64 col=0
65 for word in $line; do
66 if [[ "${word}" != "___" ]]; then
67 echo "${word},${row},${col}"
68 fi
69 col=$((col + 1))
70 done
71 row=$((row + 1))
72 done |
73 sort -n |
74 tee "${D}/${board}/layout.csv"
75done
diff --git a/keyboards/system76/readme.md b/keyboards/system76/readme.md
new file mode 100644
index 000000000..c0ebc942b
--- /dev/null
+++ b/keyboards/system76/readme.md
@@ -0,0 +1,5 @@
1# System76 Keyboards
2
3Keyboards by [System76](https://system76.com/):
4
5- [launch_1](https://system76.com/accessories/launch)
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}