Compare commits

...

665 commits
develop ... xap

Author SHA1 Message Date
zvecr
9b9dc839d2 Add in some defaults to info.json payload 2022-07-07 19:25:53 +01:00
zvecr
f913f126b4 Add in some defaults to info.json payload 2022-07-07 16:48:18 +01:00
QMK Bot
a65f6d33f7 Merge remote-tracking branch 'origin/develop' into xap 2022-07-07 12:44:02 +00:00
QMK Bot
6cbcce37e0 Merge remote-tracking branch 'origin/develop' into xap 2022-07-07 12:14:51 +00:00
QMK Bot
2828cb427c Merge remote-tracking branch 'origin/develop' into xap 2022-07-07 08:01:34 +00:00
QMK Bot
cf2d1eaee9 Merge remote-tracking branch 'origin/develop' into xap 2022-07-07 07:34:11 +00:00
QMK Bot
517eb82ed6 Merge remote-tracking branch 'origin/develop' into xap 2022-07-07 07:28:46 +00:00
QMK Bot
71208ccb09 Merge remote-tracking branch 'origin/develop' into xap 2022-07-07 05:45:40 +00:00
zvecr
ced7094ddd Add xap cli functions to test secure 2022-07-07 01:57:41 +01:00
zvecr
6ec0ff387b Update reserved tokens 2022-07-07 00:58:09 +01:00
zvecr
8b133897dc Split client out 2022-07-07 00:58:09 +01:00
QMK Bot
bf6f88182a Merge remote-tracking branch 'origin/develop' into xap 2022-07-06 18:28:13 +00:00
QMK Bot
8f7fe1c155 Merge remote-tracking branch 'origin/develop' into xap 2022-07-05 20:42:14 +00:00
QMK Bot
a23c40f921 Merge remote-tracking branch 'origin/develop' into xap 2022-07-04 23:00:39 +00:00
QMK Bot
eb98a4e917 Merge remote-tracking branch 'origin/develop' into xap 2022-07-04 20:49:54 +00:00
QMK Bot
74b84ff7cd Merge remote-tracking branch 'origin/develop' into xap 2022-07-03 23:42:06 +00:00
QMK Bot
218f67fa57 Merge remote-tracking branch 'origin/develop' into xap 2022-07-03 21:26:16 +00:00
QMK Bot
1d8a0b75fe Merge remote-tracking branch 'origin/develop' into xap 2022-07-03 18:09:02 +00:00
QMK Bot
b23f9ed0af Merge remote-tracking branch 'origin/develop' into xap 2022-07-03 04:13:01 +00:00
QMK Bot
8842fb9345 Merge remote-tracking branch 'origin/develop' into xap 2022-07-03 04:12:30 +00:00
QMK Bot
84ffb77b8f Merge remote-tracking branch 'origin/develop' into xap 2022-07-03 02:56:48 +00:00
QMK Bot
4fafb916bf Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 23:55:15 +00:00
QMK Bot
b7333ba99a Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 23:44:01 +00:00
QMK Bot
564fa77863 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 23:39:12 +00:00
QMK Bot
ee538c22b5 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 23:37:33 +00:00
QMK Bot
ec644bd932 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 23:16:03 +00:00
QMK Bot
d8d8f51848 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 23:13:38 +00:00
QMK Bot
d152bc43e2 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 23:12:12 +00:00
QMK Bot
a467771af9 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 22:19:46 +00:00
QMK Bot
7506a5a183 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 22:19:12 +00:00
QMK Bot
ea78aeee9a Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 19:11:12 +00:00
QMK Bot
b87d48dac7 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 19:10:23 +00:00
QMK Bot
47c263cf8d Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 18:10:37 +00:00
QMK Bot
436417ec7c Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 17:52:05 +00:00
QMK Bot
6dffffab3a Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 14:48:00 +00:00
QMK Bot
13af51006a Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 14:33:12 +00:00
QMK Bot
559ec50325 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 14:24:13 +00:00
QMK Bot
17ce7bb67f Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 14:17:28 +00:00
QMK Bot
063a5b8e7c Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 14:14:17 +00:00
QMK Bot
25544107aa Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 14:12:03 +00:00
QMK Bot
7bcd48f995 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 14:09:55 +00:00
QMK Bot
62e0f026ad Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 14:07:10 +00:00
QMK Bot
063e1c20ba Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 14:05:50 +00:00
QMK Bot
fba52bc924 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 14:02:18 +00:00
QMK Bot
234f47b503 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 13:59:11 +00:00
QMK Bot
6b6974ad4d Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 13:15:56 +00:00
QMK Bot
48fa7473b8 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 13:14:28 +00:00
QMK Bot
23153f959b Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 13:11:14 +00:00
QMK Bot
3f5a3b9d15 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 13:09:33 +00:00
QMK Bot
5854990f6f Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 12:54:59 +00:00
QMK Bot
db93e65e07 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 12:53:15 +00:00
QMK Bot
8671acbfeb Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 12:50:09 +00:00
QMK Bot
b78fa3fe6a Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 12:45:25 +00:00
QMK Bot
20508a9e54 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 12:43:46 +00:00
QMK Bot
53f1516036 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 12:42:08 +00:00
QMK Bot
ba34fc1afe Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 12:39:21 +00:00
QMK Bot
20933e0f15 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 12:37:36 +00:00
QMK Bot
fb64036e02 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 12:36:08 +00:00
Nick Brassel
31d8978281 Merge remote-tracking branch 'upstream/develop' into xap 2022-07-02 22:33:43 +10:00
Nick Brassel
c2b2f9feac Fix line endings. 2022-07-02 22:33:39 +10:00
QMK Bot
f0a8d431f6 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 12:19:29 +00:00
QMK Bot
2064651e27 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 12:13:49 +00:00
QMK Bot
2196d7cb9c Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 12:10:26 +00:00
QMK Bot
b908b4c4d2 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 12:09:25 +00:00
QMK Bot
37481d637b Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 12:07:55 +00:00
QMK Bot
1bf4da84dc Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 12:04:20 +00:00
QMK Bot
f617d9af39 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 12:01:52 +00:00
QMK Bot
e82c364bf0 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 12:00:04 +00:00
QMK Bot
e3e0d96f0e Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 11:57:39 +00:00
QMK Bot
c0469c7ea6 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 11:56:53 +00:00
QMK Bot
45a9ae2f20 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 11:56:16 +00:00
QMK Bot
a9e96caf2c Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 11:50:56 +00:00
QMK Bot
7e43152849 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 11:49:30 +00:00
QMK Bot
6f805de99a Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 11:46:35 +00:00
QMK Bot
88c9dbbccc Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 11:44:38 +00:00
QMK Bot
5d9db07e3a Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 11:44:01 +00:00
QMK Bot
fdc5f02ce2 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 11:28:58 +00:00
QMK Bot
fd05e07527 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 11:27:30 +00:00
QMK Bot
5ca47f9d3a Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 11:24:04 +00:00
QMK Bot
a0d1dd9d75 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 11:14:40 +00:00
QMK Bot
27c85b0f40 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 11:02:09 +00:00
QMK Bot
16c590f324 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 10:58:29 +00:00
QMK Bot
b599b6ab25 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 10:55:40 +00:00
QMK Bot
3f5ff5a0c6 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 05:19:31 +00:00
QMK Bot
2020544d88 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 04:30:20 +00:00
QMK Bot
a065805946 Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 03:23:52 +00:00
QMK Bot
fc5fcd909a Merge remote-tracking branch 'origin/develop' into xap 2022-07-02 03:21:27 +00:00
QMK Bot
b82de7a1b5 Merge remote-tracking branch 'origin/develop' into xap 2022-07-01 21:21:42 +00:00
QMK Bot
c01e62a7f0 Merge remote-tracking branch 'origin/develop' into xap 2022-07-01 14:40:10 +00:00
QMK Bot
0237e99669 Merge remote-tracking branch 'origin/develop' into xap 2022-07-01 14:37:25 +00:00
QMK Bot
204e13a466 Merge remote-tracking branch 'origin/develop' into xap 2022-07-01 06:12:18 +00:00
QMK Bot
4d5fb116e5 Merge remote-tracking branch 'origin/develop' into xap 2022-07-01 03:03:13 +00:00
QMK Bot
562e3ca35b Merge remote-tracking branch 'origin/develop' into xap 2022-06-30 22:41:13 +00:00
QMK Bot
c1b74164b7 Merge remote-tracking branch 'origin/develop' into xap 2022-06-30 22:30:29 +00:00
QMK Bot
8aa580d076 Merge remote-tracking branch 'origin/develop' into xap 2022-06-30 19:11:31 +00:00
QMK Bot
11185ecc14 Merge remote-tracking branch 'origin/develop' into xap 2022-06-30 19:08:28 +00:00
QMK Bot
f6d689a980 Merge remote-tracking branch 'origin/develop' into xap 2022-06-30 11:20:40 +00:00
QMK Bot
6c58f5098e Merge remote-tracking branch 'origin/develop' into xap 2022-06-30 02:26:05 +00:00
QMK Bot
4707117f7a Merge remote-tracking branch 'origin/develop' into xap 2022-06-30 01:08:23 +00:00
QMK Bot
b625a9c10d Merge remote-tracking branch 'origin/develop' into xap 2022-06-29 23:35:13 +00:00
QMK Bot
2b212b6308 Merge remote-tracking branch 'origin/develop' into xap 2022-06-29 21:42:57 +00:00
QMK Bot
89908ad1ca Merge remote-tracking branch 'origin/develop' into xap 2022-06-29 08:40:41 +00:00
QMK Bot
5399c9cc91 Merge remote-tracking branch 'origin/develop' into xap 2022-06-29 08:25:50 +00:00
QMK Bot
726d45ae68 Merge remote-tracking branch 'origin/develop' into xap 2022-06-29 08:18:36 +00:00
QMK Bot
1827417cbf Merge remote-tracking branch 'origin/develop' into xap 2022-06-29 06:45:46 +00:00
QMK Bot
d6fcb277ff Merge remote-tracking branch 'origin/develop' into xap 2022-06-28 22:47:55 +00:00
QMK Bot
2a6753904b Merge remote-tracking branch 'origin/develop' into xap 2022-06-28 22:29:54 +00:00
QMK Bot
ee318e5a26 Merge remote-tracking branch 'origin/develop' into xap 2022-06-28 22:14:46 +00:00
zvecr
2592402627 Use keymap folder hash to reset dynamic keymap eeprom 2022-06-27 21:44:26 +01:00
QMK Bot
2273c5b4e0 Merge remote-tracking branch 'origin/develop' into xap 2022-06-26 22:59:33 +00:00
QMK Bot
129e9c3952 Merge remote-tracking branch 'origin/develop' into xap 2022-06-26 21:18:55 +00:00
QMK Bot
6361da77f4 Merge remote-tracking branch 'origin/develop' into xap 2022-06-26 16:15:55 +00:00
QMK Bot
6bd449d436 Merge remote-tracking branch 'origin/develop' into xap 2022-06-25 20:23:01 +00:00
QMK Bot
9fcfaa950a Merge remote-tracking branch 'origin/develop' into xap 2022-06-25 19:46:45 +00:00
QMK Bot
5835b478f1 Merge remote-tracking branch 'origin/develop' into xap 2022-06-25 19:42:02 +00:00
QMK Bot
4facfe9554 Merge remote-tracking branch 'origin/develop' into xap 2022-06-25 19:35:49 +00:00
QMK Bot
9505ca5362 Merge remote-tracking branch 'origin/develop' into xap 2022-06-25 19:16:24 +00:00
QMK Bot
6a6176a0aa Merge remote-tracking branch 'origin/develop' into xap 2022-06-24 22:03:42 +00:00
QMK Bot
33da381968 Merge remote-tracking branch 'origin/develop' into xap 2022-06-24 10:40:43 +00:00
QMK Bot
ac23283d9c Merge remote-tracking branch 'origin/develop' into xap 2022-06-24 04:12:34 +00:00
zvecr
943ee73b40 Reserve user/keyboard broadcast messages 2022-06-24 00:04:08 +01:00
QMK Bot
bc66b54073 Merge remote-tracking branch 'origin/develop' into xap 2022-06-23 18:43:56 +00:00
QMK Bot
f305edce4d Merge remote-tracking branch 'origin/develop' into xap 2022-06-23 01:47:48 +00:00
QMK Bot
284f7c7e16 Merge remote-tracking branch 'origin/develop' into xap 2022-06-23 01:27:11 +00:00
QMK Bot
ab60a0bba0 Merge remote-tracking branch 'origin/develop' into xap 2022-06-23 01:24:28 +00:00
QMK Bot
b782a1f778 Merge remote-tracking branch 'origin/develop' into xap 2022-06-23 01:16:43 +00:00
QMK Bot
370a7704e9 Merge remote-tracking branch 'origin/develop' into xap 2022-06-23 00:53:23 +00:00
QMK Bot
a2c4283f9d Merge remote-tracking branch 'origin/develop' into xap 2022-06-23 00:40:28 +00:00
QMK Bot
4564769745 Merge remote-tracking branch 'origin/develop' into xap 2022-06-22 22:38:14 +00:00
zvecr
78f28900e0 Update EECONFIG version to ensure XAP init 2022-06-22 23:03:37 +01:00
zvecr
c22fedb5b2 Refactor xap client 2022-06-22 23:01:52 +01:00
zvecr
90fc901624 mermaid? 2022-06-22 22:17:25 +01:00
QMK Bot
2001fea9b9 Merge remote-tracking branch 'origin/develop' into xap 2022-06-22 20:00:15 +00:00
QMK Bot
223e01b762 Merge remote-tracking branch 'origin/develop' into xap 2022-06-22 16:41:55 +00:00
QMK Bot
ac8eb8acb3 Merge remote-tracking branch 'origin/develop' into xap 2022-06-22 14:31:33 +00:00
QMK Bot
a2f1ea37eb Merge remote-tracking branch 'origin/develop' into xap 2022-06-22 05:11:30 +00:00
QMK Bot
40d7920759 Merge remote-tracking branch 'origin/develop' into xap 2022-06-22 02:15:45 +00:00
QMK Bot
0c9bf637d5 Merge remote-tracking branch 'origin/develop' into xap 2022-06-22 01:54:10 +00:00
QMK Bot
d1d317ff4c Merge remote-tracking branch 'origin/develop' into xap 2022-06-21 22:01:01 +00:00
QMK Bot
39943571c4 Merge remote-tracking branch 'origin/develop' into xap 2022-06-21 17:54:14 +00:00
QMK Bot
b7998829d5 Merge remote-tracking branch 'origin/develop' into xap 2022-06-21 15:51:34 +00:00
QMK Bot
43adf84d65 Merge remote-tracking branch 'origin/develop' into xap 2022-06-21 12:24:16 +00:00
QMK Bot
313e7f64a3 Merge remote-tracking branch 'origin/develop' into xap 2022-06-21 03:42:46 +00:00
QMK Bot
890e25bbe2 Merge remote-tracking branch 'origin/develop' into xap 2022-06-21 03:34:31 +00:00
QMK Bot
fcb13dd0b5 Merge remote-tracking branch 'origin/develop' into xap 2022-06-21 03:26:13 +00:00
QMK Bot
d71026ccc3 Merge remote-tracking branch 'origin/develop' into xap 2022-06-21 03:17:13 +00:00
QMK Bot
cda2901457 Merge remote-tracking branch 'origin/develop' into xap 2022-06-21 03:15:37 +00:00
QMK Bot
d15db17acb Merge remote-tracking branch 'origin/develop' into xap 2022-06-21 03:12:46 +00:00
QMK Bot
21fc1b27c6 Merge remote-tracking branch 'origin/develop' into xap 2022-06-21 02:25:36 +00:00
QMK Bot
84deeb7e44 Merge remote-tracking branch 'origin/develop' into xap 2022-06-21 02:24:04 +00:00
QMK Bot
92e7cacb4c Merge remote-tracking branch 'origin/develop' into xap 2022-06-21 00:57:17 +00:00
QMK Bot
ac3fea3e0f Merge remote-tracking branch 'origin/develop' into xap 2022-06-21 00:53:34 +00:00
QMK Bot
ce9b36cbbf Merge remote-tracking branch 'origin/develop' into xap 2022-06-21 00:27:20 +00:00
QMK Bot
df7d58a12a Merge remote-tracking branch 'origin/develop' into xap 2022-06-20 23:31:58 +00:00
QMK Bot
6287939f23 Merge remote-tracking branch 'origin/develop' into xap 2022-06-20 22:25:24 +00:00
QMK Bot
103df1106b Merge remote-tracking branch 'origin/develop' into xap 2022-06-20 14:32:09 +00:00
QMK Bot
bdc2387a7d Merge remote-tracking branch 'origin/develop' into xap 2022-06-20 04:22:04 +00:00
zvecr
dab3a4fde0 Refactor based on recent discussion - Add back 'Layer Count' 2022-06-19 23:26:37 +01:00
QMK Bot
d467ac8427 Merge remote-tracking branch 'origin/develop' into xap 2022-06-19 21:33:24 +00:00
zvecr
956bd3b7ca Refactor based on recent discussion 2022-06-19 22:04:36 +01:00
QMK Bot
6b0bdc30de Merge remote-tracking branch 'origin/develop' into xap 2022-06-19 18:17:37 +00:00
QMK Bot
0b627d5e12 Merge remote-tracking branch 'origin/develop' into xap 2022-06-19 18:16:39 +00:00
QMK Bot
7d2f0172c4 Merge remote-tracking branch 'origin/develop' into xap 2022-06-19 18:00:05 +00:00
QMK Bot
45ff6e8256 Merge remote-tracking branch 'origin/develop' into xap 2022-06-18 22:33:30 +00:00
QMK Bot
36d87291dd Merge remote-tracking branch 'origin/develop' into xap 2022-06-18 21:38:41 +00:00
QMK Bot
e40a0615ca Merge remote-tracking branch 'origin/develop' into xap 2022-06-18 07:35:09 +00:00
QMK Bot
f9f7add22c Merge remote-tracking branch 'origin/develop' into xap 2022-06-18 05:31:27 +00:00
QMK Bot
1f2bbd3fdd Merge remote-tracking branch 'origin/develop' into xap 2022-06-18 01:43:06 +00:00
QMK Bot
8983559ee1 Merge remote-tracking branch 'origin/develop' into xap 2022-06-17 22:04:54 +00:00
QMK Bot
760b3b45e0 Merge remote-tracking branch 'origin/develop' into xap 2022-06-17 20:07:13 +00:00
QMK Bot
9296bcd920 Merge remote-tracking branch 'origin/develop' into xap 2022-06-17 03:51:20 +00:00
QMK Bot
2891daaf00 Merge remote-tracking branch 'origin/develop' into xap 2022-06-16 22:36:29 +00:00
QMK Bot
a7688b2549 Merge remote-tracking branch 'origin/develop' into xap 2022-06-16 22:35:10 +00:00
QMK Bot
3ed6f56cc2 Merge remote-tracking branch 'origin/develop' into xap 2022-06-16 22:04:28 +00:00
QMK Bot
d1bad9f098 Merge remote-tracking branch 'origin/develop' into xap 2022-06-16 21:47:15 +00:00
QMK Bot
e4c2489c04 Merge remote-tracking branch 'origin/develop' into xap 2022-06-16 21:17:07 +00:00
QMK Bot
a96aff3f27 Merge remote-tracking branch 'origin/develop' into xap 2022-06-16 18:21:55 +00:00
QMK Bot
c5498e34d2 Merge remote-tracking branch 'origin/develop' into xap 2022-06-16 18:21:11 +00:00
zvecr
ba7770a4e4 Fix secure symbol missing 2022-06-16 14:27:38 +01:00
QMK Bot
f6922d826c Merge remote-tracking branch 'origin/develop' into xap 2022-06-16 11:03:28 +00:00
QMK Bot
3eba8d5613 Merge remote-tracking branch 'origin/develop' into xap 2022-06-16 06:06:15 +00:00
QMK Bot
aba593d41a Merge remote-tracking branch 'origin/develop' into xap 2022-06-16 02:09:09 +00:00
QMK Bot
38f32f7afc Merge remote-tracking branch 'origin/develop' into xap 2022-06-16 02:05:37 +00:00
QMK Bot
8cc009946d Merge remote-tracking branch 'origin/develop' into xap 2022-06-15 23:56:05 +00:00
QMK Bot
e8ed843f44 Merge remote-tracking branch 'origin/develop' into xap 2022-06-15 21:45:09 +00:00
QMK Bot
8e8f5e8371 Merge remote-tracking branch 'origin/develop' into xap 2022-06-15 20:32:21 +00:00
QMK Bot
6c6950ab7f Merge remote-tracking branch 'origin/develop' into xap 2022-06-15 20:31:41 +00:00
QMK Bot
1a250df735 Merge remote-tracking branch 'origin/develop' into xap 2022-06-15 00:15:17 +00:00
QMK Bot
284fc9c3ab Merge remote-tracking branch 'origin/develop' into xap 2022-06-14 23:43:22 +00:00
QMK Bot
333d215a24 Merge remote-tracking branch 'origin/develop' into xap 2022-06-14 23:33:07 +00:00
QMK Bot
491949c303 Merge remote-tracking branch 'origin/develop' into xap 2022-06-14 13:56:11 +00:00
QMK Bot
0ee42261e6 Merge remote-tracking branch 'origin/develop' into xap 2022-06-14 11:31:51 +00:00
QMK Bot
90eb77d347 Merge remote-tracking branch 'origin/develop' into xap 2022-06-14 10:17:27 +00:00
QMK Bot
6732940f9d Merge remote-tracking branch 'origin/develop' into xap 2022-06-14 10:14:10 +00:00
QMK Bot
71a9aef016 Merge remote-tracking branch 'origin/develop' into xap 2022-06-13 20:13:39 +00:00
QMK Bot
9342369c67 Merge remote-tracking branch 'origin/develop' into xap 2022-06-13 18:31:59 +00:00
QMK Bot
ae3ea0fe81 Merge remote-tracking branch 'origin/develop' into xap 2022-06-13 05:15:57 +00:00
QMK Bot
dc9a57fee4 Merge remote-tracking branch 'origin/develop' into xap 2022-06-13 01:16:34 +00:00
QMK Bot
be2d25d9dc Merge remote-tracking branch 'origin/develop' into xap 2022-06-13 01:07:08 +00:00
QMK Bot
45d2a62f73 Merge remote-tracking branch 'origin/develop' into xap 2022-06-13 00:56:31 +00:00
QMK Bot
cbebe5a6a7 Merge remote-tracking branch 'origin/develop' into xap 2022-06-13 00:45:00 +00:00
QMK Bot
811dccc7be Merge remote-tracking branch 'origin/develop' into xap 2022-06-12 23:17:53 +00:00
QMK Bot
017829daec Merge remote-tracking branch 'origin/develop' into xap 2022-06-12 18:31:26 +00:00
QMK Bot
51bca87dd4 Merge remote-tracking branch 'origin/develop' into xap 2022-06-12 14:35:37 +00:00
QMK Bot
2f566af886 Merge remote-tracking branch 'origin/develop' into xap 2022-06-12 10:48:23 +00:00
QMK Bot
8a529cac45 Merge remote-tracking branch 'origin/develop' into xap 2022-06-11 18:26:03 +00:00
QMK Bot
4e9a2ca5f7 Merge remote-tracking branch 'origin/develop' into xap 2022-06-11 18:19:51 +00:00
QMK Bot
43ce7ab31f Merge remote-tracking branch 'origin/develop' into xap 2022-06-11 18:10:53 +00:00
QMK Bot
32d86cee7f Merge remote-tracking branch 'origin/develop' into xap 2022-06-11 17:53:45 +00:00
QMK Bot
d428f579e0 Merge remote-tracking branch 'origin/develop' into xap 2022-06-11 05:02:19 +00:00
QMK Bot
ebc9cb4ed1 Merge remote-tracking branch 'origin/develop' into xap 2022-06-11 02:08:35 +00:00
QMK Bot
14e3248a24 Merge remote-tracking branch 'origin/develop' into xap 2022-06-11 00:20:38 +00:00
QMK Bot
6f8e461cd1 Merge remote-tracking branch 'origin/develop' into xap 2022-06-10 22:25:35 +00:00
QMK Bot
452c0ba4fb Merge remote-tracking branch 'origin/develop' into xap 2022-06-10 10:45:56 +00:00
QMK Bot
55b4fa3a5a Merge remote-tracking branch 'origin/develop' into xap 2022-06-10 07:47:01 +00:00
QMK Bot
61c534947a Merge remote-tracking branch 'origin/develop' into xap 2022-06-10 03:43:59 +00:00
QMK Bot
762496d0b1 Merge remote-tracking branch 'origin/develop' into xap 2022-06-10 03:41:43 +00:00
QMK Bot
5afc776cec Merge remote-tracking branch 'origin/develop' into xap 2022-06-10 03:26:51 +00:00
QMK Bot
b6446026b5 Merge remote-tracking branch 'origin/develop' into xap 2022-06-10 01:16:59 +00:00
QMK Bot
066d7c10f1 Merge remote-tracking branch 'origin/develop' into xap 2022-06-10 01:13:50 +00:00
QMK Bot
ce55c73fac Merge remote-tracking branch 'origin/develop' into xap 2022-06-09 20:03:40 +00:00
QMK Bot
ed00870773 Merge remote-tracking branch 'origin/develop' into xap 2022-06-09 18:54:39 +00:00
QMK Bot
28c9593ce9 Merge remote-tracking branch 'origin/develop' into xap 2022-06-09 17:09:47 +00:00
QMK Bot
c7b6522c7e Merge remote-tracking branch 'origin/develop' into xap 2022-06-09 01:39:55 +00:00
QMK Bot
511c7647f7 Merge remote-tracking branch 'origin/develop' into xap 2022-06-08 21:52:47 +00:00
QMK Bot
114fadd58d Merge remote-tracking branch 'origin/develop' into xap 2022-06-08 19:22:06 +00:00
QMK Bot
47b17a15c6 Merge remote-tracking branch 'origin/develop' into xap 2022-06-08 06:24:54 +00:00
QMK Bot
5387e5068e Merge remote-tracking branch 'origin/develop' into xap 2022-06-08 01:49:26 +00:00
QMK Bot
0abb8ed990 Merge remote-tracking branch 'origin/develop' into xap 2022-06-08 00:49:10 +00:00
QMK Bot
58177d0670 Merge remote-tracking branch 'origin/develop' into xap 2022-06-07 23:43:07 +00:00
QMK Bot
60f84a89f3 Merge remote-tracking branch 'origin/develop' into xap 2022-06-07 18:34:24 +00:00
QMK Bot
91cf4a2b41 Merge remote-tracking branch 'origin/develop' into xap 2022-06-07 18:32:41 +00:00
QMK Bot
c0d291067a Merge remote-tracking branch 'origin/develop' into xap 2022-06-07 18:30:46 +00:00
QMK Bot
8ba396ad11 Merge remote-tracking branch 'origin/develop' into xap 2022-06-07 18:29:20 +00:00
QMK Bot
cc27d0b2dd Merge remote-tracking branch 'origin/develop' into xap 2022-06-07 18:23:48 +00:00
QMK Bot
1c5c6a7f78 Merge remote-tracking branch 'origin/develop' into xap 2022-06-07 18:16:07 +00:00
QMK Bot
ff2ca9cd26 Merge remote-tracking branch 'origin/develop' into xap 2022-06-07 18:02:46 +00:00
QMK Bot
fbeb7a870a Merge remote-tracking branch 'origin/develop' into xap 2022-06-07 13:33:27 +00:00
QMK Bot
555b28f9e5 Merge remote-tracking branch 'origin/develop' into xap 2022-06-06 22:31:09 +00:00
QMK Bot
bf31316957 Merge remote-tracking branch 'origin/develop' into xap 2022-06-06 00:34:25 +00:00
QMK Bot
17676d05c3 Merge remote-tracking branch 'origin/develop' into xap 2022-06-05 22:47:51 +00:00
QMK Bot
35faea32eb Merge remote-tracking branch 'origin/develop' into xap 2022-06-05 19:07:08 +00:00
QMK Bot
96284a6d63 Merge remote-tracking branch 'origin/develop' into xap 2022-06-05 09:20:18 +00:00
QMK Bot
7d522fb933 Merge remote-tracking branch 'origin/develop' into xap 2022-06-05 00:26:33 +00:00
Nick Brassel
d255c2f57f Fixup casing. 2022-06-05 08:52:01 +10:00
QMK Bot
3ff05e58df Merge remote-tracking branch 'origin/develop' into xap 2022-06-04 00:54:01 +00:00
QMK Bot
6b14a17b48 Merge remote-tracking branch 'origin/develop' into xap 2022-06-04 00:46:15 +00:00
QMK Bot
1f9200c3a7 Merge remote-tracking branch 'origin/develop' into xap 2022-06-03 11:13:57 +00:00
zvecr
ff96f67e92 format 2022-06-02 20:17:15 +01:00
QMK Bot
84672f0950 Merge remote-tracking branch 'origin/develop' into xap 2022-06-02 18:31:29 +00:00
QMK Bot
f1de138ce9 Merge remote-tracking branch 'origin/develop' into xap 2022-06-02 17:21:37 +00:00
QMK Bot
a20454c83a Merge remote-tracking branch 'origin/develop' into xap 2022-06-02 16:52:10 +00:00
QMK Bot
ded347ae04 Merge remote-tracking branch 'origin/develop' into xap 2022-06-02 16:38:50 +00:00
QMK Bot
06f5a54fd5 Merge remote-tracking branch 'origin/develop' into xap 2022-06-02 16:19:34 +00:00
QMK Bot
6d53a07674 Merge remote-tracking branch 'origin/develop' into xap 2022-06-02 13:03:55 +00:00
QMK Bot
001b4f6e3c Merge remote-tracking branch 'origin/develop' into xap 2022-05-31 23:10:43 +00:00
QMK Bot
4d48300ade Merge remote-tracking branch 'origin/develop' into xap 2022-05-31 21:39:08 +00:00
zvecr
d923483769 Bodge for breakpoint branch name 2022-05-31 18:43:21 +01:00
QMK Bot
1248afd956 Merge remote-tracking branch 'origin/develop' into xap 2022-05-31 11:33:01 +00:00
QMK Bot
72458dcd76 Merge remote-tracking branch 'origin/develop' into xap 2022-05-31 08:23:30 +00:00
QMK Bot
a06820a18d Merge remote-tracking branch 'origin/develop' into xap 2022-05-31 06:20:46 +00:00
QMK Bot
995921443a Merge remote-tracking branch 'origin/develop' into xap 2022-05-31 06:15:52 +00:00
QMK Bot
292995c8ba Merge remote-tracking branch 'origin/develop' into xap 2022-05-31 05:56:05 +00:00
QMK Bot
6579526ace Merge remote-tracking branch 'origin/develop' into xap 2022-05-31 05:38:38 +00:00
QMK Bot
f227208634 Merge remote-tracking branch 'origin/develop' into xap 2022-05-31 05:29:28 +00:00
QMK Bot
d5d72135e4 Merge remote-tracking branch 'origin/develop' into xap 2022-05-31 05:04:02 +00:00
QMK Bot
df0d0bd2ce Merge remote-tracking branch 'origin/develop' into xap 2022-05-31 04:47:33 +00:00
QMK Bot
9302ee0f81 Merge remote-tracking branch 'origin/develop' into xap 2022-05-31 04:09:52 +00:00
QMK Bot
9aadf14b31 Merge remote-tracking branch 'origin/develop' into xap 2022-05-31 02:10:05 +00:00
QMK Bot
03ca0cc8c9 Merge remote-tracking branch 'origin/develop' into xap 2022-05-30 22:44:39 +00:00
Nick Brassel
33d779cc16 Merge remote-tracking branch 'upstream/develop' into xap 2022-05-31 08:08:22 +10:00
QMK Bot
9a23ca668c Merge remote-tracking branch 'origin/develop' into xap 2022-05-28 22:16:33 +00:00
QMK Bot
08e93f5ea7 Merge remote-tracking branch 'origin/develop' into xap 2022-05-28 22:15:51 +00:00
Nick Brassel
73dbb042bf Branch point after 2022-05-28 Breaking Change. 2022-05-29 08:15:16 +10:00
QMK Bot
b11c5702fa Merge remote-tracking branch 'origin/develop' into xap 2022-05-28 22:14:34 +00:00
QMK Bot
23f72ad16c Merge remote-tracking branch 'origin/develop' into xap 2022-05-28 22:01:59 +00:00
QMK Bot
0b53879fc6 Merge remote-tracking branch 'origin/develop' into xap 2022-05-28 22:01:12 +00:00
Nick Brassel
d0ed9071a2 Branch point after 2022-05-28 Breaking Change. 2022-05-29 08:00:55 +10:00
QMK Bot
5cfb7ef396 Merge remote-tracking branch 'origin/develop' into xap 2022-05-28 21:54:25 +00:00
QMK Bot
ffdba839b0 Merge remote-tracking branch 'origin/develop' into xap 2022-05-28 21:50:59 +00:00
QMK Bot
12b2993fc2 Merge remote-tracking branch 'origin/develop' into xap 2022-05-26 20:08:52 +00:00
zvecr
f727692fd1 Render request/response in XAP docs 2022-05-26 18:22:54 +01:00
zvecr
32404cbb77 tidy xap.mk 2022-05-26 17:24:17 +01:00
QMK Bot
61fa26a45d Merge remote-tracking branch 'origin/develop' into xap 2022-05-25 12:12:35 +00:00
QMK Bot
202be7f3b5 Merge remote-tracking branch 'origin/develop' into xap 2022-05-24 17:10:34 +00:00
QMK Bot
a3f4d8064f Merge remote-tracking branch 'origin/develop' into xap 2022-05-24 01:37:05 +00:00
QMK Bot
a40942394d Merge remote-tracking branch 'origin/develop' into xap 2022-05-24 01:22:39 +00:00
zvecr
6d93facc16 bump some definitions to common 2022-05-24 01:19:16 +01:00
zvecr
d83616dce4 Validate more 2022-05-23 20:26:09 +01:00
zvecr
f44a988476 Initial validation of xap.hjson 2022-05-23 20:02:29 +01:00
QMK Bot
429c592289 Merge remote-tracking branch 'origin/develop' into xap 2022-05-23 06:22:42 +00:00
QMK Bot
d1140dd23a Merge remote-tracking branch 'origin/develop' into xap 2022-05-23 06:12:34 +00:00
QMK Bot
9a9c5df2a7 Merge remote-tracking branch 'origin/develop' into xap 2022-05-23 06:04:47 +00:00
QMK Bot
3a1b0cdd2f Merge remote-tracking branch 'origin/develop' into xap 2022-05-23 05:57:59 +00:00
QMK Bot
b620124024 Merge remote-tracking branch 'origin/develop' into xap 2022-05-23 05:07:12 +00:00
zvecr
f2d56f5ca1 Align filenames 2022-05-23 02:14:42 +01:00
zvecr
e04e31cde7 Extend make rules to include user/keyboard xap.json files 2022-05-23 01:25:32 +01:00
zvecr
b1632946c9 stub out keyboard/keymap level xap specs 2022-05-23 00:12:36 +01:00
zvecr
17dbcedc8b bind xap_broadcast_secure_status to secure_hook_quantum 2022-05-22 22:47:50 +01:00
zvecr
d95d22201a multiline description? 2022-05-22 21:12:32 +01:00
zvecr
d75330f9c1 flesh out secure docs 2022-05-22 20:55:17 +01:00
zvecr
a9468f385e stub out route and broadcast_message docs 2022-05-22 16:01:37 +01:00
QMK Bot
d9b045a6fe Merge remote-tracking branch 'origin/develop' into xap 2022-05-21 22:55:10 +00:00
QMK Bot
89ddecb2de Merge remote-tracking branch 'origin/develop' into xap 2022-05-21 16:37:53 +00:00
QMK Bot
4a71fcc98b Merge remote-tracking branch 'origin/develop' into xap 2022-05-20 17:23:52 +00:00
QMK Bot
6567aecb58 Merge remote-tracking branch 'origin/develop' into xap 2022-05-20 17:14:15 +00:00
QMK Bot
cd1d67c930 Merge remote-tracking branch 'origin/develop' into xap 2022-05-20 17:11:27 +00:00
QMK Bot
0dd025de78 Merge remote-tracking branch 'origin/develop' into xap 2022-05-20 16:15:53 +00:00
QMK Bot
badad5c65f Merge remote-tracking branch 'origin/develop' into xap 2022-05-20 16:13:17 +00:00
QMK Bot
c8ee7abd7f Merge remote-tracking branch 'origin/develop' into xap 2022-05-20 16:09:47 +00:00
QMK Bot
384a5a07c2 Merge remote-tracking branch 'origin/develop' into xap 2022-05-20 05:52:19 +00:00
QMK Bot
b6c9366ce0 Merge remote-tracking branch 'origin/develop' into xap 2022-05-20 05:51:43 +00:00
QMK Bot
5ad3150c14 Merge remote-tracking branch 'origin/develop' into xap 2022-05-20 05:45:16 +00:00
QMK Bot
e243f47a6d Merge remote-tracking branch 'origin/develop' into xap 2022-05-20 05:39:19 +00:00
QMK Bot
b666febc72 Merge remote-tracking branch 'origin/develop' into xap 2022-05-20 05:29:44 +00:00
QMK Bot
eb53b08cb4 Merge remote-tracking branch 'origin/develop' into xap 2022-05-20 04:39:20 +00:00
QMK Bot
1e8c96cea3 Merge remote-tracking branch 'origin/develop' into xap 2022-05-20 00:47:56 +00:00
QMK Bot
8356cb615f Merge remote-tracking branch 'origin/develop' into xap 2022-05-20 00:39:32 +00:00
QMK Bot
4e87e4b074 Merge remote-tracking branch 'origin/develop' into xap 2022-05-19 20:27:07 +00:00
QMK Bot
7cac1ee2d1 Merge remote-tracking branch 'origin/develop' into xap 2022-05-19 17:56:24 +00:00
QMK Bot
349a42357d Merge remote-tracking branch 'origin/develop' into xap 2022-05-19 07:49:19 +00:00
QMK Bot
57741e11fd Merge remote-tracking branch 'origin/develop' into xap 2022-05-18 19:59:08 +00:00
QMK Bot
817b2ffb8c Merge remote-tracking branch 'origin/develop' into xap 2022-05-18 17:34:31 +00:00
QMK Bot
de71c9bc0f Merge remote-tracking branch 'origin/develop' into xap 2022-05-18 10:07:52 +00:00
QMK Bot
287ff0066b Merge remote-tracking branch 'origin/develop' into xap 2022-05-17 20:51:29 +00:00
QMK Bot
f6cf52c10b Merge remote-tracking branch 'origin/develop' into xap 2022-05-17 18:50:08 +00:00
QMK Bot
5ef912ead7 Merge remote-tracking branch 'origin/develop' into xap 2022-05-17 16:14:08 +00:00
QMK Bot
237c0fdc3e Merge remote-tracking branch 'origin/develop' into xap 2022-05-17 13:21:32 +00:00
QMK Bot
af5745832b Merge remote-tracking branch 'origin/develop' into xap 2022-05-17 12:02:08 +00:00
QMK Bot
1138b51961 Merge remote-tracking branch 'origin/develop' into xap 2022-05-17 05:06:33 +00:00
QMK Bot
da07c5e44a Merge remote-tracking branch 'origin/develop' into xap 2022-05-17 05:04:09 +00:00
QMK Bot
778e7683f5 Merge remote-tracking branch 'origin/develop' into xap 2022-05-17 05:03:20 +00:00
QMK Bot
25efe0a376 Merge remote-tracking branch 'origin/develop' into xap 2022-05-17 04:46:49 +00:00
QMK Bot
74115d42f5 Merge remote-tracking branch 'origin/develop' into xap 2022-05-17 03:28:34 +00:00
QMK Bot
421a1d1ff3 Merge remote-tracking branch 'origin/develop' into xap 2022-05-17 01:46:08 +00:00
QMK Bot
6e910f5250 Merge remote-tracking branch 'origin/develop' into xap 2022-05-16 11:48:02 +00:00
QMK Bot
b181f46f93 Merge remote-tracking branch 'origin/develop' into xap 2022-05-16 05:30:56 +00:00
Nick Brassel
484ec12397 Merge remote-tracking branch 'upstream/develop' into xap 2022-05-16 09:51:49 +10:00
QMK Bot
1fee0ae414 Merge remote-tracking branch 'origin/develop' into xap 2022-05-15 19:26:56 +00:00
QMK Bot
89e0d40e3b Merge remote-tracking branch 'origin/develop' into xap 2022-05-15 11:27:31 +00:00
QMK Bot
774d3ce428 Merge remote-tracking branch 'origin/develop' into xap 2022-05-15 11:25:08 +00:00
QMK Bot
38b540d233 Merge remote-tracking branch 'origin/develop' into xap 2022-05-14 22:17:47 +00:00
QMK Bot
0129d2a36a Merge remote-tracking branch 'origin/develop' into xap 2022-05-14 22:15:26 +00:00
QMK Bot
6099b08b76 Merge remote-tracking branch 'origin/develop' into xap 2022-05-14 22:09:45 +00:00
QMK Bot
eff75ce5b7 Merge remote-tracking branch 'origin/develop' into xap 2022-05-14 22:07:50 +00:00
QMK Bot
14dfba2eef Merge remote-tracking branch 'origin/develop' into xap 2022-05-14 21:29:28 +00:00
QMK Bot
f024fdb0bc Merge remote-tracking branch 'origin/develop' into xap 2022-05-14 21:25:41 +00:00
QMK Bot
d6773a133c Merge remote-tracking branch 'origin/develop' into xap 2022-05-14 21:23:43 +00:00
QMK Bot
6932cfa3a2 Merge remote-tracking branch 'origin/develop' into xap 2022-05-14 21:22:35 +00:00
QMK Bot
15ce8c895f Merge remote-tracking branch 'origin/develop' into xap 2022-05-14 20:03:44 +00:00
QMK Bot
7a1ad96e5e Merge remote-tracking branch 'origin/develop' into xap 2022-05-14 16:15:41 +00:00
QMK Bot
9bb91fa4a3 Merge remote-tracking branch 'origin/develop' into xap 2022-05-14 06:01:05 +00:00
QMK Bot
b43cef27b3 Merge remote-tracking branch 'origin/develop' into xap 2022-05-14 05:26:46 +00:00
QMK Bot
ad7d7a2ac7 Merge remote-tracking branch 'origin/develop' into xap 2022-05-14 03:36:21 +00:00
QMK Bot
cf85b91a7f Merge remote-tracking branch 'origin/develop' into xap 2022-05-14 03:17:53 +00:00
QMK Bot
a70f8bec44 Merge remote-tracking branch 'origin/develop' into xap 2022-05-13 16:55:18 +00:00
QMK Bot
6f1097225f Merge remote-tracking branch 'origin/develop' into xap 2022-05-13 16:36:10 +00:00
QMK Bot
476e64937f Merge remote-tracking branch 'origin/develop' into xap 2022-05-13 06:37:19 +00:00
QMK Bot
d89203326b Merge remote-tracking branch 'origin/develop' into xap 2022-05-13 06:35:17 +00:00
QMK Bot
81b68fd7dc Merge remote-tracking branch 'origin/develop' into xap 2022-05-13 06:23:07 +00:00
QMK Bot
5fe5d49cda Merge remote-tracking branch 'origin/develop' into xap 2022-05-13 06:22:25 +00:00
QMK Bot
72c9cb0681 Merge remote-tracking branch 'origin/develop' into xap 2022-05-12 20:12:09 +00:00
QMK Bot
4875711c7b Merge remote-tracking branch 'origin/develop' into xap 2022-05-12 18:25:30 +00:00
QMK Bot
a4c96e446c Merge remote-tracking branch 'origin/develop' into xap 2022-05-12 06:50:31 +00:00
QMK Bot
236ac7cbd1 Merge remote-tracking branch 'origin/develop' into xap 2022-05-12 05:52:40 +00:00
QMK Bot
ea1046948d Merge remote-tracking branch 'origin/develop' into xap 2022-05-12 05:29:01 +00:00
QMK Bot
cd0c15f887 Merge remote-tracking branch 'origin/develop' into xap 2022-05-12 04:50:49 +00:00
QMK Bot
6c4c64f147 Merge remote-tracking branch 'origin/develop' into xap 2022-05-12 04:49:10 +00:00
QMK Bot
f7e436b91c Merge remote-tracking branch 'origin/develop' into xap 2022-05-12 00:10:20 +00:00
QMK Bot
0be699dde6 Merge remote-tracking branch 'origin/develop' into xap 2022-05-11 23:55:48 +00:00
QMK Bot
bce04e6318 Merge remote-tracking branch 'origin/develop' into xap 2022-05-11 23:40:46 +00:00
QMK Bot
54ce775fff Merge remote-tracking branch 'origin/develop' into xap 2022-05-11 23:38:47 +00:00
QMK Bot
9c0937da42 Merge remote-tracking branch 'origin/develop' into xap 2022-05-11 23:10:22 +00:00
QMK Bot
0d1f9d1c8c Merge remote-tracking branch 'origin/develop' into xap 2022-05-11 22:40:20 +00:00
QMK Bot
4c49aaaa70 Merge remote-tracking branch 'origin/develop' into xap 2022-05-11 19:52:48 +00:00
QMK Bot
3ea9154b0a Merge remote-tracking branch 'origin/develop' into xap 2022-05-11 11:31:16 +00:00
QMK Bot
6b5a62e15f Merge remote-tracking branch 'origin/develop' into xap 2022-05-11 11:13:59 +00:00
QMK Bot
8ed1bec0a6 Merge remote-tracking branch 'origin/develop' into xap 2022-05-11 10:58:49 +00:00
QMK Bot
1b63938411 Merge remote-tracking branch 'origin/develop' into xap 2022-05-11 10:15:47 +00:00
zvecr
a8cbda7b8a lower threshold 2022-05-11 03:25:53 +01:00
zvecr
cb7d103ba8 wider keys? 2022-05-11 01:53:47 +01:00
zvecr
68208278e0 Render layers with 'qmk info' logic 2022-05-11 01:53:47 +01:00
QMK Bot
87106e44c8 Merge remote-tracking branch 'origin/develop' into xap 2022-05-10 18:08:45 +00:00
zvecr
3d9c2fd845 Fix duplicate keys 2022-05-10 18:23:54 +01:00
QMK Bot
f88ceb5164 Merge remote-tracking branch 'origin/develop' into xap 2022-05-10 16:02:02 +00:00
QMK Bot
690bca2672 Merge remote-tracking branch 'origin/develop' into xap 2022-05-10 12:27:00 +00:00
zvecr
f9f0d84eb0 Cache xap specs 2022-05-10 03:49:01 +01:00
QMK Bot
ea850f1380 Merge remote-tracking branch 'origin/develop' into xap 2022-05-10 01:29:47 +00:00
zvecr
5028d6672a Use keycodes for xap version 2022-05-10 02:29:30 +01:00
zvecr
41a5dcbfa7 Add more DD basic keycodes 2022-05-10 01:38:14 +01:00
zvecr
ea92d5ed7d Block out basic keycodes 2022-05-09 23:51:58 +01:00
QMK Bot
218bd48ebc Merge remote-tracking branch 'origin/develop' into xap 2022-05-09 01:06:27 +00:00
QMK Bot
87b099795c Merge remote-tracking branch 'origin/develop' into xap 2022-05-07 12:02:18 +00:00
QMK Bot
1a13c379ac Merge remote-tracking branch 'origin/develop' into xap 2022-05-07 00:02:30 +00:00
QMK Bot
3cef0d587a Merge remote-tracking branch 'origin/develop' into xap 2022-05-07 00:01:29 +00:00
QMK Bot
df9f15737b Merge remote-tracking branch 'origin/develop' into xap 2022-05-06 23:59:12 +00:00
QMK Bot
c81feb6248 Merge remote-tracking branch 'origin/develop' into xap 2022-05-06 23:48:57 +00:00
zvecr
7e819d7945 specs as json? 2022-05-06 23:33:51 +01:00
zvecr
58642ff40c Publish resolved XAP specs? 2022-05-06 23:11:16 +01:00
QMK Bot
a2604fd80f Merge remote-tracking branch 'origin/develop' into xap 2022-05-06 12:19:57 +00:00
QMK Bot
de0d6378bc Merge remote-tracking branch 'origin/develop' into xap 2022-05-06 06:16:45 +00:00
zvecr
94ec23ea77 Remove requirement to quote action args 2022-05-05 22:35:04 +01:00
zvecr
cc851142fa Add cli interactive shell 2022-05-05 22:16:38 +01:00
zvecr
c65ec90484 Fix a few mistakes in docs 2022-05-05 21:05:10 +01:00
zvecr
01cd1ac71f stash 2022-05-05 12:18:57 +01:00
zvecr
c01e8ed75d stash 2022-05-05 12:18:57 +01:00
QMK Bot
de45a60432 Merge remote-tracking branch 'origin/develop' into xap 2022-05-05 10:33:00 +00:00
QMK Bot
519ba9be0c Merge remote-tracking branch 'origin/develop' into xap 2022-05-05 08:09:47 +00:00
QMK Bot
72156de175 Merge remote-tracking branch 'origin/develop' into xap 2022-05-05 02:56:53 +00:00
QMK Bot
5a11b4593b Merge remote-tracking branch 'origin/develop' into xap 2022-05-04 15:12:06 +00:00
QMK Bot
c1185d3905 Merge remote-tracking branch 'origin/develop' into xap 2022-05-04 15:10:02 +00:00
QMK Bot
f54dcc7962 Merge remote-tracking branch 'origin/develop' into xap 2022-05-03 19:49:15 +00:00
QMK Bot
d1ec7f2e0c Merge remote-tracking branch 'origin/develop' into xap 2022-05-03 10:23:20 +00:00
QMK Bot
3cc8bcb936 Merge remote-tracking branch 'origin/develop' into xap 2022-05-02 19:36:23 +00:00
QMK Bot
4d9a611780 Merge remote-tracking branch 'origin/develop' into xap 2022-05-01 18:01:01 +00:00
QMK Bot
74880dd26e Merge remote-tracking branch 'origin/develop' into xap 2022-04-29 22:57:37 +00:00
QMK Bot
b5df55448b Merge remote-tracking branch 'origin/develop' into xap 2022-04-29 10:47:08 +00:00
QMK Bot
7e2ece2409 Merge remote-tracking branch 'origin/develop' into xap 2022-04-29 08:15:08 +00:00
QMK Bot
1a9f4eb665 Merge remote-tracking branch 'origin/develop' into xap 2022-04-29 07:08:06 +00:00
QMK Bot
05195af26f Merge remote-tracking branch 'origin/develop' into xap 2022-04-29 06:59:22 +00:00
QMK Bot
f833e34bd0 Merge remote-tracking branch 'origin/develop' into xap 2022-04-29 00:32:53 +00:00
QMK Bot
e7cae3949e Merge remote-tracking branch 'origin/develop' into xap 2022-04-29 00:24:20 +00:00
QMK Bot
496da4267e Merge remote-tracking branch 'origin/develop' into xap 2022-04-28 23:37:22 +00:00
QMK Bot
db9a12c048 Merge remote-tracking branch 'origin/develop' into xap 2022-04-28 18:17:53 +00:00
QMK Bot
5066e8c0d5 Merge remote-tracking branch 'origin/develop' into xap 2022-04-28 17:39:26 +00:00
QMK Bot
90f38c1b24 Merge remote-tracking branch 'origin/develop' into xap 2022-04-28 17:36:42 +00:00
QMK Bot
27549e534f Merge remote-tracking branch 'origin/develop' into xap 2022-04-27 23:12:51 +00:00
QMK Bot
01b831d3da Merge remote-tracking branch 'origin/develop' into xap 2022-04-26 14:12:05 +00:00
QMK Bot
de85113520 Merge remote-tracking branch 'origin/develop' into xap 2022-04-25 22:55:11 +00:00
QMK Bot
90420b1e49 Merge remote-tracking branch 'origin/develop' into xap 2022-04-25 09:31:39 +00:00
QMK Bot
c8ac96c768 Merge remote-tracking branch 'origin/develop' into xap 2022-04-25 05:09:18 +00:00
QMK Bot
8e1a690079 Merge remote-tracking branch 'origin/develop' into xap 2022-04-24 13:02:28 +00:00
QMK Bot
24d394a2aa Merge remote-tracking branch 'origin/develop' into xap 2022-04-24 02:02:06 +00:00
QMK Bot
2e387d2347 Merge remote-tracking branch 'origin/develop' into xap 2022-04-23 20:58:02 +00:00
QMK Bot
20aefbdb77 Merge remote-tracking branch 'origin/develop' into xap 2022-04-22 11:51:18 +00:00
QMK Bot
1bfd694fd8 Merge remote-tracking branch 'origin/develop' into xap 2022-04-22 08:20:11 +00:00
QMK Bot
2483a049aa Merge remote-tracking branch 'origin/develop' into xap 2022-04-22 08:17:45 +00:00
QMK Bot
5900caa877 Merge remote-tracking branch 'origin/develop' into xap 2022-04-22 08:14:05 +00:00
QMK Bot
da18279390 Merge remote-tracking branch 'origin/develop' into xap 2022-04-22 07:56:37 +00:00
QMK Bot
940c53f420 Merge remote-tracking branch 'origin/develop' into xap 2022-04-22 07:49:03 +00:00
QMK Bot
c6e38f46d9 Merge remote-tracking branch 'origin/develop' into xap 2022-04-22 07:42:57 +00:00
QMK Bot
aae9ffea0c Merge remote-tracking branch 'origin/develop' into xap 2022-04-22 07:40:31 +00:00
QMK Bot
6af289bdde Merge remote-tracking branch 'origin/develop' into xap 2022-04-22 07:38:36 +00:00
QMK Bot
3706574002 Merge remote-tracking branch 'origin/develop' into xap 2022-04-22 07:37:14 +00:00
QMK Bot
317e6a3f2a Merge remote-tracking branch 'origin/develop' into xap 2022-04-22 07:34:30 +00:00
QMK Bot
c1923a7c62 Merge remote-tracking branch 'origin/develop' into xap 2022-04-22 07:33:12 +00:00
QMK Bot
a07d10f5a9 Merge remote-tracking branch 'origin/develop' into xap 2022-04-22 07:22:21 +00:00
QMK Bot
3d92e04851 Merge remote-tracking branch 'origin/develop' into xap 2022-04-22 07:18:42 +00:00
QMK Bot
f550113f25 Merge remote-tracking branch 'origin/develop' into xap 2022-04-21 21:49:59 +00:00
QMK Bot
9f9f42848d Merge remote-tracking branch 'origin/develop' into xap 2022-04-21 21:33:36 +00:00
QMK Bot
85bf9c1aa9 Merge remote-tracking branch 'origin/develop' into xap 2022-04-21 21:04:16 +00:00
QMK Bot
e486dc9278 Merge remote-tracking branch 'origin/develop' into xap 2022-04-21 16:34:52 +00:00
QMK Bot
7d83445309 Merge remote-tracking branch 'origin/develop' into xap 2022-04-21 16:10:16 +00:00
zvecr
4d895892e5 Stubs for ENCODER_MAP 2022-04-20 22:38:06 +01:00
QMK Bot
1306d58774 Merge remote-tracking branch 'origin/develop' into xap 2022-04-20 17:11:54 +00:00
QMK Bot
a49a2e3f34 Merge remote-tracking branch 'origin/develop' into xap 2022-04-20 07:47:48 +00:00
QMK Bot
68c8e61c6a Merge remote-tracking branch 'origin/develop' into xap 2022-04-19 19:20:00 +00:00
QMK Bot
bcd9b23a65 Merge remote-tracking branch 'origin/develop' into xap 2022-04-19 19:16:03 +00:00
QMK Bot
f7f9a9affe Merge remote-tracking branch 'origin/develop' into xap 2022-04-19 19:01:18 +00:00
QMK Bot
e69de0637a Merge remote-tracking branch 'origin/develop' into xap 2022-04-19 17:59:14 +00:00
QMK Bot
620c4e6eb0 Merge remote-tracking branch 'origin/develop' into xap 2022-04-19 11:31:14 +00:00
QMK Bot
9ffd4505a0 Merge remote-tracking branch 'origin/develop' into xap 2022-04-19 11:28:45 +00:00
QMK Bot
d3a4adefc4 Merge remote-tracking branch 'origin/develop' into xap 2022-04-19 11:21:15 +00:00
QMK Bot
3a8ea8a683 Merge remote-tracking branch 'origin/develop' into xap 2022-04-19 11:19:04 +00:00
QMK Bot
6a4de6a962 Merge remote-tracking branch 'origin/develop' into xap 2022-04-19 11:06:27 +00:00
QMK Bot
34ff3ed1ba Merge remote-tracking branch 'origin/develop' into xap 2022-04-19 11:04:53 +00:00
QMK Bot
a0ebf624f1 Merge remote-tracking branch 'origin/develop' into xap 2022-04-19 11:02:55 +00:00
QMK Bot
0c153a4751 Merge remote-tracking branch 'origin/develop' into xap 2022-04-19 11:01:11 +00:00
QMK Bot
e1ccd6cbd2 Merge remote-tracking branch 'origin/develop' into xap 2022-04-19 10:56:53 +00:00
QMK Bot
a80d0036f3 Merge remote-tracking branch 'origin/develop' into xap 2022-04-19 10:54:18 +00:00
QMK Bot
e95ce2fab4 Merge remote-tracking branch 'origin/develop' into xap 2022-04-19 10:40:57 +00:00
QMK Bot
97b1d49015 Merge remote-tracking branch 'origin/develop' into xap 2022-04-19 10:35:21 +00:00
QMK Bot
b6f451194e Merge remote-tracking branch 'origin/develop' into xap 2022-04-19 10:33:23 +00:00
QMK Bot
bf9facddc5 Merge remote-tracking branch 'origin/develop' into xap 2022-04-19 10:32:20 +00:00
QMK Bot
9145458eb6 Merge remote-tracking branch 'origin/develop' into xap 2022-04-19 10:29:53 +00:00
QMK Bot
892f88e7d1 Merge remote-tracking branch 'origin/develop' into xap 2022-04-19 10:18:22 +00:00
QMK Bot
0df5dae0f6 Merge remote-tracking branch 'origin/develop' into xap 2022-04-19 10:07:11 +00:00
QMK Bot
5616513800 Merge remote-tracking branch 'origin/develop' into xap 2022-04-19 08:58:27 +00:00
QMK Bot
abec2e9052 Merge remote-tracking branch 'origin/develop' into xap 2022-04-19 02:34:31 +00:00
Nick Brassel
d17aed8e82 Verify struct sizing at build time. 2022-04-19 12:33:56 +10:00
zvecr
3730ddacac Fix ARM builds due to packing inconsistencies 2022-04-19 02:07:05 +01:00
zvecr
1d96fc866d Add route for hardware_id 2022-04-19 02:07:05 +01:00
QMK Bot
81cce42118 Merge remote-tracking branch 'origin/develop' into xap 2022-04-19 00:24:13 +00:00
QMK Bot
84c3879435 Merge remote-tracking branch 'origin/develop' into xap 2022-04-19 00:19:27 +00:00
QMK Bot
36bc8c0c1a Merge remote-tracking branch 'origin/develop' into xap 2022-04-18 17:02:38 +00:00
QMK Bot
e28727d8ba Merge remote-tracking branch 'origin/develop' into xap 2022-04-18 14:47:37 +00:00
QMK Bot
bdfe1470ab Merge remote-tracking branch 'origin/develop' into xap 2022-04-18 14:40:19 +00:00
QMK Bot
4cfcd51acb Merge remote-tracking branch 'origin/develop' into xap 2022-04-18 10:52:10 +00:00
QMK Bot
3d43baf3f2 Merge remote-tracking branch 'origin/develop' into xap 2022-04-18 09:30:56 +00:00
QMK Bot
9f78fb69e4 Merge remote-tracking branch 'origin/develop' into xap 2022-04-18 09:29:32 +00:00
QMK Bot
1f212894e1 Merge remote-tracking branch 'origin/develop' into xap 2022-04-18 09:16:07 +00:00
QMK Bot
5af8b47bb9 Merge remote-tracking branch 'origin/develop' into xap 2022-04-18 09:13:21 +00:00
QMK Bot
2e3fe5f1b2 Merge remote-tracking branch 'origin/develop' into xap 2022-04-18 09:12:12 +00:00
QMK Bot
f6b7c2ebee Merge remote-tracking branch 'origin/develop' into xap 2022-04-18 09:08:51 +00:00
QMK Bot
6f134870ab Merge remote-tracking branch 'origin/develop' into xap 2022-04-18 09:08:04 +00:00
QMK Bot
b4dc49601e Merge remote-tracking branch 'origin/develop' into xap 2022-04-18 07:38:38 +00:00
QMK Bot
a608afdcf4 Merge remote-tracking branch 'origin/develop' into xap 2022-04-17 19:55:02 +00:00
QMK Bot
b0fbcdd8a4 Merge remote-tracking branch 'origin/develop' into xap 2022-04-17 00:37:39 +00:00
QMK Bot
379dac6ade Merge remote-tracking branch 'origin/develop' into xap 2022-04-16 23:10:03 +00:00
zvecr
3c20f00238 Merge remote-tracking branch 'origin/develop' into xap 2022-04-16 23:49:41 +01:00
QMK Bot
c79bfc6ed9 Merge remote-tracking branch 'origin/develop' into xap 2022-04-16 17:41:49 +00:00
QMK Bot
78acf2c1a1 Merge remote-tracking branch 'origin/develop' into xap 2022-04-16 01:24:26 +00:00
QMK Bot
425b80ddd6 Merge remote-tracking branch 'origin/develop' into xap 2022-04-14 18:25:13 +00:00
QMK Bot
331b232ba1 Merge remote-tracking branch 'origin/develop' into xap 2022-04-14 17:02:35 +00:00
QMK Bot
37f92fad65 Merge remote-tracking branch 'origin/develop' into xap 2022-04-14 16:27:46 +00:00
QMK Bot
3f0c7f1921 Merge remote-tracking branch 'origin/develop' into xap 2022-04-14 05:43:34 +00:00
QMK Bot
ff198b384b Merge remote-tracking branch 'origin/develop' into xap 2022-04-14 03:28:33 +00:00
QMK Bot
fcb549f36c Merge remote-tracking branch 'origin/develop' into xap 2022-04-13 16:46:28 +00:00
QMK Bot
3503d1c27b Merge remote-tracking branch 'origin/develop' into xap 2022-04-13 16:28:29 +00:00
QMK Bot
f951028c94 Merge remote-tracking branch 'origin/develop' into xap 2022-04-13 14:32:05 +00:00
QMK Bot
7f4f38dcac Merge remote-tracking branch 'origin/develop' into xap 2022-04-13 14:31:23 +00:00
QMK Bot
c6511ad9bc Merge remote-tracking branch 'origin/develop' into xap 2022-04-13 14:26:13 +00:00
QMK Bot
6add5a8720 Merge remote-tracking branch 'origin/develop' into xap 2022-04-13 14:12:47 +00:00
Nick Brassel
85331d56ec Merge remote-tracking branch 'upstream/develop' into xap 2022-04-13 20:11:53 +10:00
QMK Bot
e7af9fb808 Merge remote-tracking branch 'origin/develop' into xap 2022-04-13 07:47:13 +00:00
QMK Bot
af26928941 Merge remote-tracking branch 'origin/develop' into xap 2022-04-13 07:05:28 +00:00
QMK Bot
858a55e5d8 Merge remote-tracking branch 'origin/develop' into xap 2022-04-13 07:04:18 +00:00
QMK Bot
3cbf1e455f Merge remote-tracking branch 'origin/develop' into xap 2022-04-13 06:41:30 +00:00
QMK Bot
7d023e4951 Merge remote-tracking branch 'origin/develop' into xap 2022-04-13 06:38:11 +00:00
QMK Bot
1d8ae78806 Merge remote-tracking branch 'origin/develop' into xap 2022-04-13 06:37:35 +00:00
QMK Bot
c1f92cc536 Merge remote-tracking branch 'origin/develop' into xap 2022-04-13 06:21:13 +00:00
QMK Bot
b3bce44385 Merge remote-tracking branch 'origin/develop' into xap 2022-04-13 06:05:39 +00:00
QMK Bot
cd8329ae9f Merge remote-tracking branch 'origin/develop' into xap 2022-04-13 06:04:53 +00:00
QMK Bot
1c13c5885c Merge remote-tracking branch 'origin/develop' into xap 2022-04-13 05:51:41 +00:00
QMK Bot
7107489242 Merge remote-tracking branch 'origin/develop' into xap 2022-04-13 05:47:14 +00:00
QMK Bot
e59b61e8af Merge remote-tracking branch 'origin/develop' into xap 2022-04-13 05:26:22 +00:00
zvecr
b0b6594ded secure keycodes? 2022-04-13 00:42:50 +01:00
zvecr
2563e7b2a0 format 2022-04-12 13:53:12 +01:00
zvecr
f249e33f70 Short term bodge to force dynamic_keymap population 2022-04-12 02:08:18 +01:00
zvecr
12e8c8d8ee Process entire unlock sequence - revert changes to SECURE_UNLOCK_SEQUENCE 2022-04-12 01:58:02 +01:00
zvecr
320f161c72 Process entire unlock sequence 2022-04-12 01:49:30 +01:00
zvecr
d19285019d All other subsystems are disabled during unlock 2022-04-12 01:37:31 +01:00
QMK Bot
b1eab6d3a0 Merge remote-tracking branch 'origin/develop' into xap 2022-04-11 13:12:57 +00:00
QMK Bot
d21f84c521 Merge remote-tracking branch 'origin/develop' into xap 2022-04-11 10:20:02 +00:00
QMK Bot
90429d9657 Merge remote-tracking branch 'origin/develop' into xap 2022-04-11 09:11:31 +00:00
zvecr
1ea2928d2a XAP messages extend timeout? 2022-04-11 02:14:19 +01:00
zvecr
ffb0575eb8 stub out secure data driven config 2022-04-11 01:59:48 +01:00
zvecr
05b5a4c23a format 2022-04-11 01:08:37 +01:00
zvecr
79db2df228 Allow secure to be slightly more optional 2022-04-11 01:07:22 +01:00
zvecr
3e4de1ebd0 format 2022-04-11 00:53:31 +01:00
zvecr
89fab427c4 stub out secure as its own feature 2022-04-11 00:43:18 +01:00
QMK Bot
2c068d08dd Merge remote-tracking branch 'origin/develop' into xap 2022-04-09 22:27:30 +00:00
QMK Bot
aefe98bfea Merge remote-tracking branch 'origin/develop' into xap 2022-04-09 18:50:25 +00:00
QMK Bot
5e33ca8f7c Merge remote-tracking branch 'origin/develop' into xap 2022-04-09 18:07:21 +00:00
Drashna Jael're
9ac3445454
Merge remote-tracking branch 'origin/develop' into xap 2022-04-08 20:34:54 -07:00
QMK Bot
66beedde80 Merge remote-tracking branch 'origin/develop' into xap 2022-04-06 14:46:51 +00:00
QMK Bot
8d67b9bcc5 Merge remote-tracking branch 'origin/develop' into xap 2022-04-06 10:14:08 +00:00
QMK Bot
9c4119bc15 Merge remote-tracking branch 'origin/develop' into xap 2022-04-06 10:07:51 +00:00
QMK Bot
02b7726323 Merge remote-tracking branch 'origin/develop' into xap 2022-04-06 00:23:37 +00:00
QMK Bot
98b668c675 Merge remote-tracking branch 'origin/develop' into xap 2022-04-06 00:09:12 +00:00
QMK Bot
2de248fc06 Merge remote-tracking branch 'origin/develop' into xap 2022-04-05 21:15:35 +00:00
QMK Bot
af5c3c948a Merge remote-tracking branch 'origin/develop' into xap 2022-04-05 19:14:54 +00:00
QMK Bot
3be420fd9b Merge remote-tracking branch 'origin/develop' into xap 2022-04-05 18:46:49 +00:00
zvecr
18aa0d2d19 Merge remote-tracking branch 'origin/develop' into xap 2022-04-05 19:37:58 +01:00
zvecr
194d6d65be format 2022-04-05 19:17:57 +01:00
QMK Bot
d339f182ba Merge remote-tracking branch 'origin/develop' into xap 2022-04-05 18:07:34 +00:00
zvecr
c1b57354f6 Stub out more of broadcast messages 2022-04-05 18:54:28 +01:00
QMK Bot
0d59f8b42d Merge remote-tracking branch 'origin/develop' into xap 2022-04-04 19:28:10 +00:00
QMK Bot
2abb01084f Merge remote-tracking branch 'origin/develop' into xap 2022-04-04 19:21:11 +00:00
QMK Bot
4de0f7d9b5 Merge remote-tracking branch 'origin/develop' into xap 2022-04-04 18:57:29 +00:00
QMK Bot
b87f85a81a Merge remote-tracking branch 'origin/develop' into xap 2022-04-03 21:02:17 +00:00
QMK Bot
f0daafb741 Merge remote-tracking branch 'origin/develop' into xap 2022-04-03 18:18:16 +00:00
QMK Bot
99d097de97 Merge remote-tracking branch 'origin/develop' into xap 2022-04-03 18:03:28 +00:00
QMK Bot
e3afa07ca9 Merge remote-tracking branch 'origin/develop' into xap 2022-04-03 17:45:49 +00:00
QMK Bot
df823f8306 Merge remote-tracking branch 'origin/develop' into xap 2022-04-03 17:23:30 +00:00
QMK Bot
567949de19 Merge remote-tracking branch 'origin/develop' into xap 2022-04-03 12:19:29 +00:00
QMK Bot
6a026f8653 Merge remote-tracking branch 'origin/develop' into xap 2022-04-02 21:29:19 +00:00
QMK Bot
72ea4cf574 Merge remote-tracking branch 'origin/develop' into xap 2022-04-02 21:10:21 +00:00
QMK Bot
715da89a6e Merge remote-tracking branch 'origin/develop' into xap 2022-04-02 14:54:56 +00:00
zvecr
c9eae1d384 format 2022-04-02 00:11:22 +01:00
zvecr
e7d9d6675c Implement codegen for more data types - codegen for return_execute stubs removed 2022-04-01 23:38:13 +01:00
QMK Bot
d968e6c005 Merge remote-tracking branch 'origin/develop' into xap 2022-04-01 09:27:14 +00:00
zvecr
c5842ab9b5 stub out return_execute with zero args 2022-04-01 00:44:27 +01:00
zvecr
e111b9d017 Use slightly more unique data name 2022-03-31 23:31:01 +01:00
zvecr
2c8c9c9928 And sort out docs gen too 2022-03-31 23:16:56 +01:00
zvecr
53052228df Add types codegen 2022-03-31 22:34:25 +01:00
Nick Brassel
ffcdfc6c03 Swap info.json.gz length to #define. 2022-04-01 08:15:33 +11:00
zvecr
81a53ac5b6 gen RESPONSE_FLAG defines 2022-03-31 21:08:18 +01:00
zvecr
0f5ced0521 claim back a few bytes 2022-03-31 19:14:35 +01:00
zvecr
fe1a4a52d4 clang 2022-03-31 01:23:19 +01:00
zvecr
646fdc7d17 Rework code gen for return of dynamic variables 2022-03-31 01:18:26 +01:00
zvecr
73d2228524 format 2022-03-30 23:25:33 +01:00
zvecr
6269c6b51c partial gen for return_execute 2022-03-30 23:20:14 +01:00
QMK Bot
5a099e1ad7 Merge remote-tracking branch 'origin/develop' into xap 2022-03-30 20:55:54 +00:00
QMK Bot
43d6473e00 Merge remote-tracking branch 'origin/develop' into xap 2022-03-30 19:01:01 +00:00
zvecr
51e09235a2 clang 2022-03-30 02:28:03 +01:00
zvecr
7f128c5286 Remove some assumptions on packet format 2022-03-30 02:09:19 +01:00
zvecr
22b8299230 Fix up print_dotted_output dict handling 2022-03-30 00:43:41 +01:00
zvecr
a65ea1a711 Fix codegen for non led boards 2022-03-30 00:43:41 +01:00
zvecr
13ee88dd21 Data driven g_led config 2022-03-30 00:43:41 +01:00
QMK Bot
578fe2d029 Merge remote-tracking branch 'origin/develop' into xap 2022-03-29 19:19:54 +00:00
zvecr
56c9f7b7ff format 2022-03-29 19:25:16 +01:00
zvecr
05911e9908 bodge 'qmk xap -l' for windows 2022-03-29 18:36:08 +01:00
zvecr
7262333857 Use generic 'dump_lines' 2022-03-28 21:18:17 +01:00
zvecr
ff1bb76537 basic info.json handling 2022-03-28 21:06:16 +01:00
QMK Bot
1fbbd72c14 Merge remote-tracking branch 'origin/develop' into xap 2022-03-28 16:47:50 +00:00
QMK Bot
780526848b Merge remote-tracking branch 'origin/develop' into xap 2022-03-27 22:07:57 +00:00
QMK Bot
fc45ff1f07 Merge remote-tracking branch 'origin/develop' into xap 2022-03-27 20:29:43 +00:00
QMK Bot
933888780e Merge remote-tracking branch 'origin/develop' into xap 2022-03-27 02:04:07 +00:00
Drashna Jael're
80102c0e83
Merge remote-tracking branch 'origin/develop' into xap 2022-03-26 13:37:01 -07:00
Drashna Jael're
15e0964108
Merge remote-tracking branch 'origin/develop' into xap 2022-03-26 10:32:27 -07:00
QMK Bot
620716b106 Merge remote-tracking branch 'origin/develop' into xap 2022-03-24 18:09:08 +00:00
QMK Bot
cabdab5e75 Merge remote-tracking branch 'origin/develop' into xap 2022-03-24 17:17:16 +00:00
QMK Bot
9c4e294887 Merge remote-tracking branch 'origin/develop' into xap 2022-03-24 05:24:44 +00:00
QMK Bot
d6f2f02428 Merge remote-tracking branch 'origin/develop' into xap 2022-03-23 18:44:29 +00:00
QMK Bot
6a9f0961d6 Merge remote-tracking branch 'origin/develop' into xap 2022-03-23 16:49:55 +00:00
QMK Bot
c325737d9c Merge remote-tracking branch 'origin/develop' into xap 2022-03-23 06:03:02 +00:00
QMK Bot
3d4575a06d Merge remote-tracking branch 'origin/develop' into xap 2022-03-23 05:07:29 +00:00
QMK Bot
4d28978dee Merge remote-tracking branch 'origin/develop' into xap 2022-03-22 16:40:19 +00:00
QMK Bot
728305e961 Merge remote-tracking branch 'origin/develop' into xap 2022-03-22 10:48:49 +00:00
zvecr
e31c605bf7 revert split logic 2022-03-22 00:04:12 +00:00
zvecr
f872fbea7e Merge remote-tracking branch 'origin/develop' into xap 2022-03-21 12:46:36 +00:00
QMK Bot
6281446aee Merge remote-tracking branch 'origin/develop' into xap 2022-03-21 08:06:45 +00:00
QMK Bot
0dba850d43 Merge remote-tracking branch 'origin/develop' into xap 2022-03-20 17:25:13 +00:00
QMK Bot
9ad318efc6 Merge remote-tracking branch 'origin/develop' into xap 2022-03-20 17:06:57 +00:00
QMK Bot
c5e26a8040 Merge remote-tracking branch 'origin/develop' into xap 2022-03-20 17:03:48 +00:00
QMK Bot
9ed471fd33 Merge remote-tracking branch 'origin/develop' into xap 2022-03-20 04:20:44 +00:00
zvecr
b365cbce15 Merge in keymap level to XAP info.json payload 2022-03-20 01:25:04 +00:00
QMK Bot
29f349b90b Merge remote-tracking branch 'origin/develop' into xap 2022-03-19 20:59:36 +00:00
QMK Bot
ded6a379b2 Merge remote-tracking branch 'origin/develop' into xap 2022-03-19 17:42:45 +00:00
QMK Bot
5df35467b4 Merge remote-tracking branch 'origin/develop' into xap 2022-03-19 16:38:46 +00:00
QMK Bot
8dfe1134cf Merge remote-tracking branch 'origin/develop' into xap 2022-03-19 11:53:39 +00:00
QMK Bot
dd2c2bfa97 Merge remote-tracking branch 'origin/develop' into xap 2022-03-19 11:52:33 +00:00
QMK Bot
3cb3d5b0c9 Merge remote-tracking branch 'origin/develop' into xap 2022-03-19 06:57:50 +00:00
QMK Bot
b73f5d3da6 Merge remote-tracking branch 'origin/develop' into xap 2022-03-19 06:53:44 +00:00
zvecr
70c9905cb6 clang 2022-03-18 21:43:01 +00:00
zvecr
a5204887a8 maybe vusb 2022-03-18 21:37:23 +00:00
zvecr
c27edf4e64 vusb prep 2022-03-18 20:23:28 +00:00
zvecr
aaf4fcbe5a Merge chibios support 2022-03-18 19:51:20 +00:00
QMK Bot
b73a0b14aa Merge remote-tracking branch 'origin/develop' into xap 2022-03-18 18:50:26 +00:00
QMK Bot
aff373bab2 Merge remote-tracking branch 'origin/develop' into xap 2022-03-18 17:25:53 +00:00
zvecr
4d4b013e5b Fixup after merge 2022-03-18 17:03:54 +00:00
zvecr
5bb6173cc7 Fixup after merge 2022-03-18 17:01:02 +00:00
zvecr
e5e1e54f39 Merge remote-tracking branch 'origin/develop' into xap 2022-03-18 16:57:34 +00:00
zvecr
72602a3443 Fixup after merge 2022-03-18 01:53:39 +00:00
zvecr
2e8db66201 Merge remote-tracking branch 'origin/develop' into xap 2022-03-18 01:20:16 +00:00
QMK Bot
46256e08eb Merge remote-tracking branch 'origin/develop' into xap 2022-03-17 21:20:11 +00:00
QMK Bot
770c7ab20e Merge remote-tracking branch 'origin/develop' into xap 2022-03-16 23:46:07 +00:00
zvecr
31c4864705 Crude CLI device discovery 2022-03-16 19:45:42 +00:00
QMK Bot
68efb1635c Merge remote-tracking branch 'origin/develop' into xap 2022-03-16 15:35:27 +00:00
zvecr
9fd4db1fc7 fix up for pytest - remove fstring escaping 2022-03-16 10:58:21 +00:00
QMK Bot
5b2db70d65 Merge remote-tracking branch 'origin/develop' into xap 2022-03-16 04:10:00 +00:00
QMK Bot
7301950bcf Merge remote-tracking branch 'origin/develop' into xap 2022-03-16 03:58:52 +00:00
QMK Bot
d5b6b82361 Merge remote-tracking branch 'origin/develop' into xap 2022-03-16 03:15:37 +00:00
QMK Bot
1bb00c1609 Merge remote-tracking branch 'origin/develop' into xap 2022-03-16 03:14:32 +00:00
zvecr
52d3b9dcc5 fix up for pytest 2022-03-16 00:10:40 +00:00
QMK Bot
cddcd8d735 Merge remote-tracking branch 'origin/develop' into xap 2022-03-16 00:07:59 +00:00
zvecr
7e8f0d49ea Temp bodge for unit tests? 2022-03-15 23:57:59 +00:00
QMK Bot
7183516a2b Merge remote-tracking branch 'origin/develop' into xap 2022-03-15 23:28:59 +00:00
zvecr
2b4724bd83 Convert info_json_gz.h generation to CLI 2022-03-15 17:59:12 +00:00
zvecr
b96b862ef9 Merge remote-tracking branch 'origin/develop' into xap 2022-03-15 13:27:35 +00:00
Nick Brassel
c3ac89d1c9 qmk format-c, qmk format-python 2022-03-09 20:01:20 +11:00
Nick Brassel
575d8c19fc Merge remote-tracking branch 'upstream/develop' into xap 2022-03-09 19:47:31 +11:00
Nick Brassel
6c7afbb859 Migrate XAP docs generator into CLI now that most logic is in Jinja2 files. 2022-02-16 10:53:35 +11:00
Nick Brassel
69e9c80ec3 Reworked docs rendering using jinja2. 2022-02-15 05:19:13 +11:00
Nick Brassel
1e723e6647 Output logging. 2022-02-14 09:46:17 +11:00
Nick Brassel
c9ec8a1309 Merge remote-tracking branch 'upstream/develop' into xap 2022-02-14 09:01:22 +11:00
Nick Brassel
dcf4bf6d29 Merge remote-tracking branch 'upstream/develop' into xap 2022-02-03 03:15:01 +11:00
Nick Brassel
bf66b91433 Merge remote-tracking branch 'upstream/develop' into xap 2021-11-28 12:56:46 +11:00
Nick Brassel
ec9a78cc4a Doco update. 2021-09-16 11:43:19 +10:00
Nick Brassel
942d9f6a09 Merge remote-tracking branch 'upstream/develop' into xap 2021-09-16 07:46:55 +10:00
Nick Brassel
3c66b9b0ec Merge remote-tracking branch 'upstream/develop' into xap 2021-09-15 11:40:29 +10:00
Nick Brassel
5aae5a767f Use the correct input type. 2021-09-15 08:49:51 +10:00
Nick Brassel
437559cd03 Swap to fnvhash due to deps. 2021-09-15 08:45:14 +10:00
Nick Brassel
eba91c6e28 Initial implementation of XAP protocol. 2021-08-11 21:08:32 +10:00
65 changed files with 5817 additions and 42 deletions

View file

@ -352,7 +352,12 @@ $(KEYBOARD_OUTPUT)/src/layouts.h: $(INFO_JSON_FILES)
$(eval CMD=$(QMK_BIN) generate-layouts --quiet --keyboard $(KEYBOARD) --output $(KEYBOARD_OUTPUT)/src/layouts.h)
@$(BUILD_CMD)
generated-files: $(KEYBOARD_OUTPUT)/src/info_config.h $(KEYBOARD_OUTPUT)/src/default_keyboard.c $(KEYBOARD_OUTPUT)/src/default_keyboard.h $(KEYBOARD_OUTPUT)/src/layouts.h
$(KEYBOARD_OUTPUT)/src/keymap_hash.h:
@$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD)
$(eval CMD=$(QMK_BIN) generate-keymap-hash -q -o "$(KEYMAP_OUTPUT)/src/keymap_hash.h" -kb $(KEYBOARD) -km $(KEYMAP))
@$(BUILD_CMD)
generated-files: $(KEYBOARD_OUTPUT)/src/keymap_hash.h $(KEYBOARD_OUTPUT)/src/info_config.h $(KEYBOARD_OUTPUT)/src/default_keyboard.c $(KEYBOARD_OUTPUT)/src/default_keyboard.h $(KEYBOARD_OUTPUT)/src/layouts.h
.INTERMEDIATE : generated-files

View file

@ -44,6 +44,10 @@ else ifeq ($(strip $(DEBUG_MATRIX_SCAN_RATE_ENABLE)), api)
OPT_DEFS += -DDEBUG_MATRIX_SCAN_RATE
endif
ifeq ($(strip $(XAP_ENABLE)), yes)
include $(BUILDDEFS_PATH)/xap.mk
endif
AUDIO_ENABLE ?= no
ifeq ($(strip $(AUDIO_ENABLE)), yes)
ifeq ($(PLATFORM),CHIBIOS)
@ -866,6 +870,20 @@ ifeq ($(strip $(USBPD_ENABLE)), yes)
endif
endif
ifeq ($(strip $(XAP_ENABLE)), yes)
ifeq ($(strip $(VIA_ENABLE)), yes)
$(error 'XAP_ENABLE = $(XAP_ENABLE)' deprecates 'VIA_ENABLE = $(VIA_ENABLE)'. Please set 'VIA_ENABLE = no')
endif
OPT_DEFS += -DXAP_ENABLE
OPT_DEFS += -DBOOTLOADER_JUMP_SUPPORTED
DYNAMIC_KEYMAP_ENABLE := yes
SECURE_ENABLE := yes
EMBED_INFO_JSON := yes
VPATH += $(QUANTUM_DIR)/xap
SRC += $(QUANTUM_DIR)/xap/xap.c $(QUANTUM_DIR)/xap/xap_handlers.c
endif
BLUETOOTH_ENABLE ?= no
VALID_BLUETOOTH_DRIVER_TYPES := BluefruitLE RN42 custom
ifeq ($(strip $(BLUETOOTH_ENABLE)), yes)

41
builddefs/xap.mk Normal file
View file

@ -0,0 +1,41 @@
# Copyright 2022 Nick Brassel (@tzarc)
# SPDX-License-Identifier: GPL-2.0-or-later
XAP_FILES := $(shell ls -1 data/xap/* | sort | xargs echo)
ifneq ("$(wildcard $(KEYBOARD_PATH_1)/xap.hjson)","")
XAP_FILES += $(KEYBOARD_PATH_1)/xap.hjson
endif
ifneq ("$(wildcard $(KEYBOARD_PATH_2)/xap.hjson)","")
XAP_FILES += $(KEYBOARD_PATH_2)/xap.hjson
endif
ifneq ("$(wildcard $(KEYBOARD_PATH_3)/xap.hjson)","")
XAP_FILES += $(KEYBOARD_PATH_3)/xap.hjson
endif
ifneq ("$(wildcard $(KEYBOARD_PATH_4)/xap.hjson)","")
XAP_FILES += $(KEYBOARD_PATH_4)/xap.hjson
endif
ifneq ("$(wildcard $(KEYBOARD_PATH_5)/xap.hjson)","")
XAP_FILES += $(KEYBOARD_PATH_5)/xap.hjson
endif
ifneq ("$(wildcard $(KEYMAP_PATH)/xap.hjson)","")
XAP_FILES += $(KEYMAP_PATH)/xap.hjson
endif
$(KEYMAP_OUTPUT)/src/config_blob_gz.h: $(INFO_JSON_FILES)
@$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD)
$(eval CMD=$(QMK_BIN) xap-generate-qmk-blob-h -o "$(KEYMAP_OUTPUT)/src/config_blob_gz.h" -kb $(KEYBOARD) -km $(KEYMAP))
@$(BUILD_CMD)
$(KEYMAP_OUTPUT)/src/xap_generated.inl: $(XAP_FILES)
@$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD)
$(eval CMD=$(QMK_BIN) xap-generate-qmk-inc -o "$(KEYMAP_OUTPUT)/src/xap_generated.inl" -kb $(KEYBOARD) -km $(KEYMAP))
@$(BUILD_CMD)
$(KEYMAP_OUTPUT)/src/xap_generated.h: $(XAP_FILES)
@$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD)
$(eval CMD=$(QMK_BIN) xap-generate-qmk-h -o "$(KEYMAP_OUTPUT)/src/xap_generated.h" -kb $(KEYBOARD) -km $(KEYMAP))
@$(BUILD_CMD)
generated-files: $(KEYMAP_OUTPUT)/src/config_blob_gz.h $(KEYMAP_OUTPUT)/src/xap_generated.inl $(KEYMAP_OUTPUT)/src/xap_generated.h
VPATH += $(KEYMAP_OUTPUT)/src

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,7 @@
{
"secure": {
"unlock_sequence": [ [0,0] ],
"unlock_timeout": 5000,
"idle_timeout": 60000
}
}

View file

@ -12,6 +12,12 @@
"minLength": 1,
"pattern": "^[0-9a-z_]*$"
},
"define": {
"type": "string",
"minLength": 2,
"maxLength": 50,
"pattern": "^[A-Z_]*$"
},
"hex_number_2d": {
"type": "string",
"pattern": "^0x[0-9A-F]{2}$"
@ -24,6 +30,10 @@
"type": "string",
"pattern": "^[0-9]{1,2}\\.[0-9]\\.[0-9]$"
},
"text_unsigned_int": {
"type": "string",
"pattern": "^[0-8]+$"
},
"text_identifier": {
"type": "string",
"minLength": 1,

262
data/schemas/xap.jsonschema Normal file
View file

@ -0,0 +1,262 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "qmk.xap.v1",
"title": "XAP Spec",
"definitions": {
"data_type": {
"oneOf": [
{
"enum": [
"bool",
"u8",
"u16",
"u32",
"u64",
"struct",
"string"
]
},
{
"type": "string",
"pattern": "^u\\d{1,2}\\[\\d{1,2}\\]*$"
}
]
},
"router_type": {
"enum": [
"command",
"router"
]
},
"permission": {
"enum": [
"secure",
"ignore"
]
},
"struct": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"type"
],
"properties": {
"name": {
"type": "string"
},
"type": {
"type": "string"
}
}
}
},
"route": {
"type": "object",
"propertyNames": {
"$ref": "qmk.definitions.v1#/hex_number_2d"
},
"additionalProperties": {
"type": "object",
"required": [
"type",
"define"
],
"additionalProperties": false,
"properties": {
"type": {
"$ref": "#/definitions/router_type"
},
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"define": {
"$ref": "qmk.definitions.v1#/define"
},
"permissions": {
"$ref": "#/definitions/permission"
},
"enable_if_preprocessor": {
"type": "string"
},
"request_type": {
"$ref": "#/definitions/data_type"
},
"request_struct_length": {
"type": "number"
},
"request_purpose": {
"type": "string"
},
"return_type": {
"$ref": "#/definitions/data_type"
},
"request_struct_members": {
"$ref": "#definitions/struct"
},
"return_struct_length": {
"type": "number"
},
"return_constant": {
"type": [
"array",
"string"
]
},
"return_struct_members": {
"$ref": "#definitions/struct"
},
"return_purpose": {
"type": "string"
},
"return_execute": {
"type": "string"
},
"routes": {
"$ref": "#/definitions/route"
}
}
}
}
},
"type": "object",
"additionalProperties": false,
"required": [
"version"
],
"properties": {
"version": {
"$ref": "qmk.definitions.v1#/bcd_version"
},
"define": {
"$ref": "qmk.definitions.v1#/define"
},
"uses": {
"type": "object",
"additionalProperties": {
"$ref": "qmk.definitions.v1#/bcd_version"
}
},
"documentation": {
"type": "object",
"properties": {
"order": {
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": {
"type": "string"
}
},
"term_definitions": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"type_docs": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"type_definitions": {
"type": "object",
"additionalProperties": {
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"type": {
"$ref": "#/definitions/data_type"
},
"struct_length": {
"type": "number"
},
"struct_members": {
"$ref": "#definitions/struct"
}
}
}
},
"response_flags": {
"type": "object",
"additionalProperties": false,
"properties": {
"define_prefix": {
"$ref": "qmk.definitions.v1#/define"
},
"bits": {
"type": "object",
"propertyNames": {
"$ref": "qmk.definitions.v1#/text_unsigned_int"
},
"additionalProperties": {
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"type": "string"
},
"define": {
"$ref": "qmk.definitions.v1#/define"
},
"description": {
"type": "string"
}
}
}
}
}
},
"broadcast_messages": {
"type": "object",
"additionalProperties": false,
"properties": {
"define_prefix": {
"$ref": "qmk.definitions.v1#/define"
},
"messages": {
"type": "object",
"propertyNames": {
"$ref": "qmk.definitions.v1#/hex_number_2d"
},
"additionalProperties": {
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"type": "string"
},
"define": {
"$ref": "qmk.definitions.v1#/define"
},
"description": {
"type": "string"
},
"return_type": {
"$ref": "#/definitions/data_type"
}
}
}
}
}
},
"routes": {
"$ref": "#/definitions/route"
}
}
}

View file

@ -0,0 +1,4 @@
{%- for id, message in xap.broadcast_messages.messages | dictsort %}
### {{ message.name }} - `{{ id }}`
{{ message.description }}
{%- endfor %}

View file

@ -0,0 +1,7 @@
{%- for item in xap.documentation.order -%}
{%- if not item[0:1] == '!' -%}
{{ xap.documentation.get(item) }}
{% else %}
{%- include item[1:] %}
{% endif %}
{% endfor %}

View file

@ -0,0 +1,9 @@
|{% for bitnum, bitinfo in xap.response_flags.bits | dictsort | reverse %} Bit {{ bitnum }} |{% endfor %}
|{% for bitnum, bitinfo in xap.response_flags.bits | dictsort | reverse %} -- |{% endfor %}
|{% for bitnum, bitinfo in xap.response_flags.bits | dictsort | reverse %} `{{ bitinfo.define }}` |{%- endfor %}
{% for bitnum, bitinfo in xap.response_flags.bits | dictsort | reverse %}
{%- if bitinfo.define != "-" -%}
* Bit {{ bitnum }} (`{{ bitinfo.define }}`): {{ bitinfo.description }}
{% endif %}
{%- endfor %}

View file

@ -0,0 +1,8 @@
{%- if subroute.request_type == 'struct' -%}
__Request:__
{%- for member in subroute.request_struct_members -%}
<br>{{ "&nbsp;"|safe*4 }}* {{ member.name }}: `{{ member.type }}`
{%- endfor -%}
{%- elif subroute.request_type -%}
__Request:__ `{{ subroute.request_type }}`
{%- endif -%}

View file

@ -0,0 +1,8 @@
{%- if subroute.return_type == 'struct' -%}
__Response:__
{%- for member in subroute.return_struct_members -%}
<br>{{ "&nbsp;"|safe*4 }}* {{ member.name }}: `{{ member.type }}`
{%- endfor -%}
{%- elif subroute.return_type -%}
__Response:__ `{{ subroute.return_type }}`
{%- endif -%}

View file

@ -0,0 +1,12 @@
{%- for id, route in xap.routes | dictsort %}
### {{ route.name }} - `{{ id }}`
{{ route.description }}
{% if route.routes %}
| Name | Route | Tags | Payloads | Description |
| -- | -- | -- | -- | -- |
{%- for subid, subroute in route.routes | dictsort %}
| {{ subroute.name }} | `{{ id }} {{ subid }}` | {% if 'secure' == subroute.permissions %}__Secure__{% endif %} | {%- include 'route_request.md.j2' -%}{%- if subroute.return_type and subroute.request_type -%}<br><br>{% endif %}{%- include 'route_response.md.j2' -%} | {{ subroute.description.replace('\n', '<br>') }}|
{%- endfor %}
{% endif %}
{%- endfor %}

View file

@ -0,0 +1,8 @@
| Name | Definition |
| -- | -- |
{%- for type, definition in xap.term_definitions | dictsort %}
| _{{ type }}_ | {{ definition }} |
{%- endfor %}
{%- for type, definition in xap.type_definitions | dictsort %}
| _{{ definition.name }}_ | {{ definition.description }}{% if 'struct' == definition.type %} Takes the format:{% for item in definition.struct_members %}<br>`{{ item.type }}` - {{ item.name }}{%- endfor %}{% endif %} |
{%- endfor %}

View file

@ -0,0 +1,5 @@
| Name | Definition |
| -- | -- |
{%- for type, definition in xap.type_docs | dictsort %}
| _{{ type }}_ | {{ definition }} |
{%- endfor %}

237
data/xap/xap_0.0.1.hjson Executable file
View file

@ -0,0 +1,237 @@
{
version: 0.0.1
// Needed for table generation
define: XAP_ROUTE
// Documentation section is used purely for `qmk xap-generate-docs`.
documentation: {
order: [
page_header
type_docs
!type_docs.md.j2
term_definitions
!term_definitions.md.j2
request_response
reserved_tokens
response_flags
!response_flags.md.j2
example_conversation
routes
!routes.md.j2
]
page_header:
'''
# QMK Firmware XAP Specs
This document describes the requirements of the QMK XAP ("extensible application protocol") API.
'''
type_docs:
'''
## Types
**All integral types are little-endian.**
'''
term_definitions:
'''
## Definitions
This list defines the terms used across the entire set of XAP protocol documentation.
'''
request_response:
'''
## Requests and Responses
Communication generally follows a request/response pattern.
Each request needs to include a _token_ -- this `u16` value prefixes each outbound request from the host application and its corresponding response.
This allows response messages to be correlated with their request, even if multiple host applications are communicating with the firmware simultaneously.
Host applications should randomly generate a token ID for **every** outbound request, unless using a reserved token defined below.
To ensure host interoperability, valid token values are within the range `0x0100`-`0xFFFF`.
This token is followed by a `u8` signifying the length of data in the request.
'''
// This documentation section reserved for next version
reserved_tokens: ''
response_flags:
'''
Response messages will always be prefixed by the originating request _token_, directly followed by that request's _response flags_, then the response payload length:
'''
example_conversation:
'''
### Example "conversation":
**Request** -- version query:
| Byte | 0 | 1 | 2 | 3 | 4 |
| --- | --- | --- | --- | --- | --- |
| **Purpose** | Token | Token | Payload Length | Route | Route |
| **Value** | `0x43` | `0x2B` | `0x02` | `0x00` | `0x00` |
**Response** -- matching token, successful flag, payload of `0x03170192` = 3.17.192:
| Byte | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| **Purpose** | Token | Token | Response Flags | Payload Length | Payload | Payload | Payload | Payload |
| **Value** | `0x43` | `0x2B` | `0x01` | `0x04` | `0x92` | `0x01` | `0x17` | `0x03` |
'''
routes:
'''
## Routes
Subsystem validity should be queried through the “Enabled-in-firmware subsystem query” under the QMK subsystem (route=0x00,0x01).
This is the primary method for determining if a subsystem has been enabled in the running firmware.
'''
}
type_docs: {
u8:
'''
An unsigned 8-bit integral (octet, or byte), commonly seen as `uint8_t` from _stdint.h_.
'''
u16:
'''
An unsigned 16-bit integral, commonly seen as `uint16_t` from _stdint.h_.
'''
u32:
'''
An unsigned 32-bit integral, commonly seen as `uint32_t` from _stdint.h_.
'''
"type[n]":
'''
An array of `type`, with array extent of `N` -- e.g. `u8[2]` signifies two consecutive octets.
'''
}
term_definitions: {
Subsystem:
'''
A high-level area of functionality within XAP.
'''
Route:
'''
A sequence of _IDs_ describing the route to invoke a _handler_.
'''
Handler:
'''
A piece of code that is executed when a specific _route_ is received.
'''
Response:
'''
The data sent back to the host during execution of a _handler_.
'''
Payload:
'''
Any received data appended to the _route_, which gets delivered to the _handler_ when received.
'''
}
type_definitions: {
identifier: {
name: ID
description: A single octet / 8-bit byte, representing Subsystem or Route index.
type: u8
}
response_flags: {
name: Response Flags
description: An `u8` containing the status of the request.
type: u8
}
token: {
name: Token
description: A `u16` associated with a specific request as well as its corresponding response. Valid token values are within the range `0x0100`-`0xFFFF`.
type: u16
}
request_header: {
name: Request Header
description: Packet format for inbound data.
type: struct
struct_length: 3
struct_members: [
{
type: token
name: token
},
{
type: u8
name: length
}
]
}
response_header: {
name: Response Header
description: Packet format for outbound data.
type: struct
struct_length: 4
struct_members: [
{
type: token
name: token
},
{
type: response_flags
name: flags
},
{
type: u8
name: length
}
]
}
}
response_flags: {
define_prefix: XAP_RESPONSE_FLAG
bits: {
0: {
name: Success
define: SUCCESS
description:
'''
When this bit is set, the request was successfully handled. If not set, all payload data should be disregarded, and the request retried if appropriate (with a new token).
'''
}
}
}
routes: {
0x00: {
type: router
name: XAP
define: XAP
description:
'''
This subsystem is always present, and provides the ability to query information about the XAP protocol of the connected device.
'''
routes: {
0x00: {
type: command
name: Version Query
define: VERSION_QUERY
description:
'''
XAP protocol version query.
* Returns the BCD-encoded version in the format of XX.YY.ZZZZ => `0xXXYYZZZZ`
* e.g. 3.2.115 will match `0x03020115`, or bytes {0x15,0x01,0x02,0x03}.
'''
return_type: u32
return_purpose: bcd-version
return_constant: XAP_BCD_VERSION
}
}
}
}
}

385
data/xap/xap_0.1.0.hjson Executable file
View file

@ -0,0 +1,385 @@
{
version: 0.1.0
uses: {
keycodes: 0.0.1
}
documentation: {
order: [
broadcast_messages
!broadcast_messages.md.j2
]
reserved_tokens:
'''
Two token values are reserved: `0xFFFE` and `0xFFFF`:
* `0xFFFE`: A message sent by a host application may use this token if no response is to be sent -- a "fire and forget" message.
* `0xFFFF`: Signifies a "broadcast" message sent by the firmware without prompting from the host application. Broadcast messages are defined later in this document.
Any request will generate at least one corresponding response, with the exception of messages using reserved tokens. Maximum total message length is 128 bytes due to RAM constraints.
'''
broadcast_messages:
'''
## Broadcast messages
Broadcast messages may be sent by the firmware to the host, without a corresponding inbound request. Each broadcast message uses the token `0xFFFF`, and does not expect a response from the host. Tokens are followed by an _ID_ signifying the type of broadcast, with corresponding _payload_.
'''
}
response_flags: {
bits: {
1: {
name: Secure Failure
define: SECURE_FAILURE
description:
'''
When this bit is set, the requested _route_ was marked _secure_ but an _unlock sequence_ has not completed.
'''
}
6: {
name: Unlocking
define: UNLOCK_IN_PROGRESS
description:
'''
When this bit is set, an _unlock sequence_ is in progress.
'''
}
7: {
name: Unlocked
define: UNLOCKED
description:
'''
When this bit is set, an _unlock sequence_ has completed, and _secure routes_ may be invoked.
'''
}
}
}
type_docs: {
u64:
'''
An unsigned 64-bit integral, commonly seen as `uint64_t` from _stdint.h_.
'''
"struct{}":
'''
A structure of data, packing different objects together. Data is "compacted" -- there are no padding bytes between fields. Equivalent to a packed C-style `struct`. The order in which they're defined matches the order of the data in the response packet.
'''
}
term_definitions: {
Capability:
'''
A way to determine if certain functionality is enabled in the firmware. Any _subsystem_ that provides build-time restriction of functionality must provide a _route_ for a _capabilities query_.
'''
"Secure Route":
'''
A _route_ which has potentially destructive consequences, necessitating prior approval by the user before executing.
'''
"Unlock sequence":
'''
A physical sequence initiated by the user to enable execution of _secure routes_.
'''
}
type_definitions: {
broadcast_header: {
name: Broadcast Header
description: Packet format for broadcast messages.
type: struct
struct_length: 4
struct_members: [
{
type: token
name: token
},
{
type: u8
name: type
},
{
type: u8
name: length
}
]
}
}
broadcast_messages: {
define_prefix: XAP_BROADCAST
messages: {
0x00: {
name: Log message
define: LOG_MESSAGE
description:
'''
Replicates and replaces the same functionality as if using the standard QMK `CONSOLE_ENABLE = yes` in `rules.mk`. Normal prints within the firmware will manifest as log messages broadcast to the host. `hid_listen` will not be functional with XAP enabled.
Log message payloads include a `u8` signifying the length of the text, followed by the `u8[Length]` containing the text itself.
**Example Log Broadcast** -- log message "Hello QMK!"
| Byte | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| **Purpose** | Token | Token | Broadcast Type | Length | Payload | Payload | Payload | Payload | Payload | Payload | Payload | Payload | Payload | Payload |
| **Value** | `0xFF` | `0xFF` | `0x00` | `0x0A`(10) | `0x48`(H) | `0x65`(e) | `0x6C`(l) | `0x6C`(l) | `0x6F`(o) | `0x20`(&nbsp;) | `0x51`(Q) | `0x4D`(M) | `0x4B`(K) | `0x21`(!) |
'''
}
0x01: {
name: Secure Status
define: SECURE_STATUS
description:
'''
Secure status has changed. Payloads include a `u8` matching a 'Secure Status' request.
**Example Secure Status Broadcast** -- secure "Unlocking"
| Byte | 0 | 1 | 2 | 3 |
| --- | --- | --- | --- | --- |
| **Purpose** | Token | Token | Broadcast Type | Secure Status |
| **Value** | `0xFF` | `0xFF` | `0x01` | `0x01` |
'''
return_type: u8
}
0x02: {
name: Keyboard
define: KB
description:
'''
Reserved for vendor-specific functionality. No messages are defined by XAP.
'''
},
0x03: {
name: User
define: USER
description:
'''
Reserved for user-specific functionality. No messages are defined by XAP.
'''
}
}
}
routes: {
0x00: {
routes: {
0x01: {
type: command
name: Capabilities Query
define: CAPABILITIES_QUERY
description:
'''
XAP subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.
'''
return_type: u32
return_purpose: capabilities
return_constant: XAP_ROUTE_XAP_CAPABILITIES
}
0x02: {
type: command
name: Enabled subsystem query
define: SUBSYSTEM_QUERY
description:
'''
XAP protocol subsystem query. Each bit should be considered as a "usable" subsystem. For example, checking `(value & (1 << XAP_ROUTE_QMK) != 0)` means the QMK subsystem is enabled and available for querying.
'''
return_type: u32
return_purpose: capabilities
return_constant: XAP_ROUTE_CAPABILITIES
}
0x03: {
type: command
name: Secure Status
define: SECURE_STATUS
description:
'''
Query secure route status
* 0 means secure routes are disabled
* 1 means unlock sequence initiated but incomplete
* 2 means secure routes are allowed
* any other value should be interpreted as disabled
'''
permissions: ignore
return_type: u8
return_execute: secure_status
}
0x04: {
type: command
name: Secure Unlock
define: SECURE_UNLOCK
description: Initiate secure route unlock sequence
return_execute: secure_unlock
}
0x05: {
type: command
name: Secure Lock
define: SECURE_LOCK
permissions: ignore
description: Disable secure routes
return_execute: secure_lock
}
}
},
0x01: {
type: router
name: QMK
define: QMK
description:
'''
This subsystem is always present, and provides the ability to address QMK-specific functionality.
'''
routes: {
0x00: {
type: command
name: Version Query
define: VERSION_QUERY
description:
'''
QMK protocol version query.
* Returns the BCD-encoded version in the format of XX.YY.ZZZZ => `0xXXYYZZZZ`
* e.g. 3.2.115 will match `0x03020115`, or bytes {0x15,0x01,0x02,0x03}.
'''
return_type: u32
return_purpose: bcd-version
return_constant: QMK_BCD_VERSION
}
0x01: {
type: command
name: Capabilities Query
define: CAPABILITIES_QUERY
description:
'''
QMK subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.
'''
return_type: u32
return_purpose: capabilities
return_constant: XAP_ROUTE_QMK_CAPABILITIES
}
0x02: {
type: command
name: Board identifiers
define: BOARD_IDENTIFIERS
description:
'''
Retrieves the set of identifying information for the board.
'''
return_type: struct
return_struct_length: 10
return_struct_members: [
{
type: u16
name: Vendor ID
},
{
type: u16
name: Product ID
},
{
type: u16
name: Product Version
},
{
type: u32
name: QMK Unique Identifier
}
]
return_constant: [
VENDOR_ID
PRODUCT_ID
DEVICE_VER
XAP_KEYBOARD_IDENTIFIER
]
}
0x03: {
type: command
name: Board Manufacturer
define: BOARD_MANUFACTURER
description: Retrieves the name of the manufacturer
return_type: string
return_constant: QSTR(MANUFACTURER)
}
0x04: {
type: command
name: Product Name
define: PRODUCT_NAME
description: Retrieves the product name
return_type: string
return_constant: QSTR(PRODUCT)
}
0x05: {
type: command
name: Config Blob Length
define: CONFIG_BLOB_LEN
description: Retrieves the length of the configuration data bundled within the firmware
return_type: u32
return_constant: CONFIG_BLOB_GZ_LEN
}
0x06: {
type: command
name: Config Blob Chunk
define: CONFIG_BLOB_CHUNK
description: Retrieves a chunk of the configuration data bundled within the firmware
request_type: u16
request_purpose: offset
return_type: u8[32]
return_execute: get_config_blob_chunk
}
0x07: {
type: command
name: Jump to bootloader
define: BOOTLOADER_JUMP
permissions: secure
enable_if_preprocessor: defined(BOOTLOADER_JUMP_SUPPORTED)
description:
'''
Jump to bootloader
May not be present if QMK capabilities query returns “true”, then jump to bootloader is supported
* 0 means secure routes are disabled, and should be considered as a failure
* 1 means successful, board will jump to bootloader
'''
return_type: u8
return_execute: request_bootloader_jump
}
0x08: {
type: command
name: Hardware Identifier
define: HARDWARE_ID
description: Retrieves a unique identifier for the board.
return_type: u32[4]
return_execute: get_hardware_id
}
}
},
0x02: {
type: router
name: Keyboard
define: KB
description:
'''
This subsystem is always present, and reserved for vendor-specific functionality. No routes are defined by XAP.
'''
routes: {
}
},
0x03: {
type: router
name: User
define: USER
description:
'''
This subsystem is always present, and reserved for user-specific functionality. No routes are defined by XAP.
'''
routes: {
}
}
}
}

201
data/xap/xap_0.2.0.hjson Executable file
View file

@ -0,0 +1,201 @@
{
version: 0.2.0
routes: {
0x04: {
type: router
name: Keymap
define: KEYMAP
description:
'''
This subsystem allows for query of currently configured keycodes.
'''
routes: {
0x01: {
type: command
name: Capabilities Query
define: CAPABILITIES_QUERY
description:
'''
Keymap subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.
'''
return_type: u32
return_purpose: capabilities
return_constant: XAP_ROUTE_KEYMAP_CAPABILITIES
}
0x02: {
type: command
name: Get Layer Count
define: GET_LAYER_COUNT
description: Query maximum number of layers that can be addressed within the keymap.
return_type: u8
return_execute: keymap_get_layer_count
}
0x03: {
type: command
name: Get Keycode
define: GET_KEYMAP_KEYCODE
description: Query the Keycode at the requested location.
request_type: struct
request_struct_length: 3
request_struct_members: [
{
type: u8
name: Layer
},
{
type: u8
name: Row
},
{
type: u8
name: Column
}
]
return_type: u16
return_execute: get_keymap_keycode
}
0x04: {
type: command
name: Get Encoder Keycode
define: GET_ENCODER_KEYCODE
description: Query the Keycode at the requested location.
enable_if_preprocessor: defined(ENCODER_MAP_ENABLE)
request_type: struct
request_struct_length: 3
request_struct_members: [
{
type: u8
name: Layer
},
{
type: u8
name: Encoder
},
{
type: u8
name: Clockwise
}
]
return_type: u16
return_execute: get_encoder_keycode
}
}
}
0x05: {
type: router
name: Remapping
define: REMAPPING
description:
'''
This subsystem allows for live reassignment of keycodes without rebuilding the firmware.
'''
enable_if_preprocessor: defined(DYNAMIC_KEYMAP_ENABLE)
routes: {
0x01: {
type: command
name: Capabilities Query
define: CAPABILITIES_QUERY
description:
'''
Remapping subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.
'''
return_type: u32
return_purpose: capabilities
return_constant: XAP_ROUTE_REMAPPING_CAPABILITIES
}
0x02: {
type: command
name: Get Layer Count
define: GET_DYNAMIC_LAYER_COUNT
description: Query maximum number of layers that can be addressed within the keymap.
return_type: u8
return_constant: DYNAMIC_KEYMAP_LAYER_COUNT
}
0x03: {
type: command
name: Set Keycode
define: SET_KEYMAP_KEYCODE
description: Modify the Keycode at the requested location.
permissions: secure
request_type: struct
request_struct_length: 5
request_struct_members: [
{
type: u8
name: Layer
},
{
type: u8
name: Row
},
{
type: u8
name: Column
},
{
type: u16
name: Keycode
}
]
return_execute: dynamic_keymap_set_keycode
}
0x04: {
type: command
name: Set Encoder Keycode
define: SET_ENCODER_KEYCODE
permissions: secure
description: Modify the Keycode at the requested location.
enable_if_preprocessor: defined(ENCODER_MAP_ENABLE)
request_type: struct
request_struct_length: 5
request_struct_members: [
{
type: u8
name: Layer
},
{
type: u8
name: Encoder
},
{
type: u8
name: Clockwise
},
{
type: u16
name: Keycode
}
]
return_execute: dynamic_encoder_set_keycode
}
}
}
0x06: {
type: router
name: Lighting
define: LIGHTING
description:
'''
This subsystem allows for control over the lighting subsystem.
'''
enable_if_preprocessor: defined(RGBLIGHT_ENABLE) || defined(RGB_MATRIX_ENABLE)
routes: {
0x01: {
type: command
name: Capabilities Query
define: CAPABILITIES_QUERY
description:
'''
Lighting subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.
'''
return_type: u32
return_purpose: capabilities
return_constant: XAP_ROUTE_LIGHTING_CAPABILITIES
}
}
}
}
}

View file

@ -92,6 +92,7 @@
* [Unicode](feature_unicode.md)
* [Userspace](feature_userspace.md)
* [WPM Calculation](feature_wpm.md)
* [XAP](xap.md)
* Hardware Features
* Displays

View file

@ -128,6 +128,8 @@
<script src="//unpkg.com/docsify/lib/docsify.min.js"></script>
<script src="//unpkg.com/docsify/lib/plugins/search.min.js"></script>
<script src="//unpkg.com/docsify/lib/plugins/emoji.min.js"></script>
<script src="//unpkg.com/mermaid/dist/mermaid.js"></script>
<script src="//unpkg.com/docsify-mermaid@latest/dist/docsify-mermaid.js"></script>
<script src="//unpkg.com/docsify-tabs@1"></script>
<script src="//unpkg.com/docsify-copy-code@2"></script>
<script src="//unpkg.com/docsify-toc@1.1.0/dist/toc.js"></script>
@ -143,5 +145,6 @@
navigator.serviceWorker.register('sw.js')
}
</script>
<script>mermaid.initialize({ startOnLoad: true });</script>
</body>
</html>

95
docs/xap.md Normal file
View file

@ -0,0 +1,95 @@
# XAP
XAP (“extensible application protocol”) API intends to provide access to various QMK subsystems.
## Overview
```mermaid
%%{init: {'themeVariables': { 'fontSize': '24px'}}}%%
flowchart LR
dev[QMK Device] <-- XAP --> host[Host Computer]
```
The intention is to provide access to QMK subsystems through a versioned and documented protocol.
## Protocol Reference
[protocol_versions](xap_protocol.md ':include')
## Clients
TODO
## CLI
The QMK CLI provides a few XAP specific commands for diagnosis purposes.
### List Connected Devices
Simple
```
$ qmk xap --list
Ψ Available devices:
Ψ 7844:8450 KPrepublic XD84 Pro [API:0.2.0] LOCKED
```
Verbose
```
$ qmk --verbose xap --list
Ψ Available devices:
Ψ 7844:8450 KPrepublic XD84 Pro [API:0.2.0] LOCKED
_id: 553831323538150A2113000000000000
backlight.pin: F5
bootloader: atmel-dfu
community_layouts: 75_ansi, 75_iso
debounce: 5
diode_direction: COL2ROW
features.audio: False
features.backlight: True
features.bootmagic: True
features.command: False
features.console: False
features.extrakey: True
features.mousekey: False
features.nkro: True
features.rgblight: True
indicators.caps_lock: B2
keyboard_folder: xiudi/xd84pro
keyboard_name: XD84 Pro
layouts: LAYOUT_75_ansi, LAYOUT_75_iso, LAYOUT_all
maintainer: qmk
manufacturer: KPrepublic
matrix_pins.cols: B1, B3, B4, B5, B6, B7, C6, C7, D4, D6, D7, E6, F0, F1, F7
matrix_pins.rows: D0, D1, D2, D3, D5, F4
matrix_pins.unused: B0, E2
matrix_size.cols: 15
matrix_size.rows: 6
mouse_key.enabled: False
platform: unknown
processor: atmega32u4
processor_type: avr
protocol: LUFA
rgblight.animations.all: False
rgblight.led_count: 12
rgblight.pin: F6
rgblight.sleep: False
url:
usb.device_ver: 0x0001
usb.device_version: 0.0.1
usb.pid: 0x8450
usb.vid: 0x7844
```
### Interactive shell
```
$ qmk xap -i
Ψ Connected to:7844:8450 KPrepublic XD84 Pro
Welcome to the XAP shell. Type help or ? to list commands.
Ψ> help
Documented commands (type help <topic>):
========================================
EOF about exit help keycode keymap layer listen unlock
Ψ>
```

85
docs/xap_0.0.1.md Normal file
View file

@ -0,0 +1,85 @@
# QMK Firmware XAP Specs
This document describes the requirements of the QMK XAP ("extensible application protocol") API.
## Types
**All integral types are little-endian.**
| Name | Definition |
| -- | -- |
| _type[n]_ | An array of `type`, with array extent of `N` -- e.g. `u8[2]` signifies two consecutive octets. |
| _u16_ | An unsigned 16-bit integral, commonly seen as `uint16_t` from _stdint.h_. |
| _u32_ | An unsigned 32-bit integral, commonly seen as `uint32_t` from _stdint.h_. |
| _u8_ | An unsigned 8-bit integral (octet, or byte), commonly seen as `uint8_t` from _stdint.h_. |
## Definitions
This list defines the terms used across the entire set of XAP protocol documentation.
| Name | Definition |
| -- | -- |
| _Handler_ | A piece of code that is executed when a specific _route_ is received. |
| _Payload_ | Any received data appended to the _route_, which gets delivered to the _handler_ when received. |
| _Response_ | The data sent back to the host during execution of a _handler_. |
| _Route_ | A sequence of _IDs_ describing the route to invoke a _handler_. |
| _Subsystem_ | A high-level area of functionality within XAP. |
| _ID_ | A single octet / 8-bit byte, representing Subsystem or Route index. |
| _Request Header_ | Packet format for inbound data. Takes the format:<br>`token` - token<br>`u8` - length |
| _Response Flags_ | An `u8` containing the status of the request. |
| _Response Header_ | Packet format for outbound data. Takes the format:<br>`token` - token<br>`response_flags` - flags<br>`u8` - length |
| _Token_ | A `u16` associated with a specific request as well as its corresponding response. Valid token values are within the range `0x0100`-`0xFFFF`. |
## Requests and Responses
Communication generally follows a request/response pattern.
Each request needs to include a _token_ -- this `u16` value prefixes each outbound request from the host application and its corresponding response.
This allows response messages to be correlated with their request, even if multiple host applications are communicating with the firmware simultaneously.
Host applications should randomly generate a token ID for **every** outbound request, unless using a reserved token defined below.
To ensure host interoperability, valid token values are within the range `0x0100`-`0xFFFF`.
This token is followed by a `u8` signifying the length of data in the request.
Response messages will always be prefixed by the originating request _token_, directly followed by that request's _response flags_, then the response payload length:
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
| -- | -- | -- | -- | -- | -- | -- | -- |
| `-` | `-` | `-` | `-` | `-` | `-` | `-` | `SUCCESS` |
* Bit 0 (`SUCCESS`): When this bit is set, the request was successfully handled. If not set, all payload data should be disregarded, and the request retried if appropriate (with a new token).
### Example "conversation":
**Request** -- version query:
| Byte | 0 | 1 | 2 | 3 | 4 |
| --- | --- | --- | --- | --- | --- |
| **Purpose** | Token | Token | Payload Length | Route | Route |
| **Value** | `0x43` | `0x2B` | `0x02` | `0x00` | `0x00` |
**Response** -- matching token, successful flag, payload of `0x03170192` = 3.17.192:
| Byte | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| **Purpose** | Token | Token | Response Flags | Payload Length | Payload | Payload | Payload | Payload |
| **Value** | `0x43` | `0x2B` | `0x01` | `0x04` | `0x92` | `0x01` | `0x17` | `0x03` |
## Routes
Subsystem validity should be queried through the “Enabled-in-firmware subsystem query” under the QMK subsystem (route=0x00,0x01).
This is the primary method for determining if a subsystem has been enabled in the running firmware.
### XAP - `0x00`
This subsystem is always present, and provides the ability to query information about the XAP protocol of the connected device.
| Name | Route | Tags | Payloads | Description |
| -- | -- | -- | -- | -- |
| Version Query | `0x00 0x00` | |__Response:__ `u32`| XAP protocol version query.<br><br>* Returns the BCD-encoded version in the format of XX.YY.ZZZZ => `0xXXYYZZZZ`<br> * e.g. 3.2.115 will match `0x03020115`, or bytes {0x15,0x01,0x02,0x03}.|

157
docs/xap_0.1.0.md Normal file
View file

@ -0,0 +1,157 @@
# QMK Firmware XAP Specs
This document describes the requirements of the QMK XAP ("extensible application protocol") API.
## Types
**All integral types are little-endian.**
| Name | Definition |
| -- | -- |
| _struct{}_ | A structure of data, packing different objects together. Data is "compacted" -- there are no padding bytes between fields. Equivalent to a packed C-style `struct`. The order in which they're defined matches the order of the data in the response packet. |
| _type[n]_ | An array of `type`, with array extent of `N` -- e.g. `u8[2]` signifies two consecutive octets. |
| _u16_ | An unsigned 16-bit integral, commonly seen as `uint16_t` from _stdint.h_. |
| _u32_ | An unsigned 32-bit integral, commonly seen as `uint32_t` from _stdint.h_. |
| _u64_ | An unsigned 64-bit integral, commonly seen as `uint64_t` from _stdint.h_. |
| _u8_ | An unsigned 8-bit integral (octet, or byte), commonly seen as `uint8_t` from _stdint.h_. |
## Definitions
This list defines the terms used across the entire set of XAP protocol documentation.
| Name | Definition |
| -- | -- |
| _Capability_ | A way to determine if certain functionality is enabled in the firmware. Any _subsystem_ that provides build-time restriction of functionality must provide a _route_ for a _capabilities query_. |
| _Handler_ | A piece of code that is executed when a specific _route_ is received. |
| _Payload_ | Any received data appended to the _route_, which gets delivered to the _handler_ when received. |
| _Response_ | The data sent back to the host during execution of a _handler_. |
| _Route_ | A sequence of _IDs_ describing the route to invoke a _handler_. |
| _Secure Route_ | A _route_ which has potentially destructive consequences, necessitating prior approval by the user before executing. |
| _Subsystem_ | A high-level area of functionality within XAP. |
| _Unlock sequence_ | A physical sequence initiated by the user to enable execution of _secure routes_. |
| _Broadcast Header_ | Packet format for broadcast messages. Takes the format:<br>`token` - token<br>`u8` - type<br>`u8` - length |
| _ID_ | A single octet / 8-bit byte, representing Subsystem or Route index. |
| _Request Header_ | Packet format for inbound data. Takes the format:<br>`token` - token<br>`u8` - length |
| _Response Flags_ | An `u8` containing the status of the request. |
| _Response Header_ | Packet format for outbound data. Takes the format:<br>`token` - token<br>`response_flags` - flags<br>`u8` - length |
| _Token_ | A `u16` associated with a specific request as well as its corresponding response. Valid token values are within the range `0x0100`-`0xFFFF`. |
## Requests and Responses
Communication generally follows a request/response pattern.
Each request needs to include a _token_ -- this `u16` value prefixes each outbound request from the host application and its corresponding response.
This allows response messages to be correlated with their request, even if multiple host applications are communicating with the firmware simultaneously.
Host applications should randomly generate a token ID for **every** outbound request, unless using a reserved token defined below.
To ensure host interoperability, valid token values are within the range `0x0100`-`0xFFFF`.
This token is followed by a `u8` signifying the length of data in the request.
Two token values are reserved: `0xFFFE` and `0xFFFF`:
* `0xFFFE`: A message sent by a host application may use this token if no response is to be sent -- a "fire and forget" message.
* `0xFFFF`: Signifies a "broadcast" message sent by the firmware without prompting from the host application. Broadcast messages are defined later in this document.
Any request will generate at least one corresponding response, with the exception of messages using reserved tokens. Maximum total message length is 128 bytes due to RAM constraints.
Response messages will always be prefixed by the originating request _token_, directly followed by that request's _response flags_, then the response payload length:
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
| -- | -- | -- | -- | -- | -- | -- | -- |
| `UNLOCKED` | `UNLOCK_IN_PROGRESS` | `-` | `-` | `-` | `-` | `SECURE_FAILURE` | `SUCCESS` |
* Bit 7 (`UNLOCKED`): When this bit is set, an _unlock sequence_ has completed, and _secure routes_ may be invoked.
* Bit 6 (`UNLOCK_IN_PROGRESS`): When this bit is set, an _unlock sequence_ is in progress.
* Bit 1 (`SECURE_FAILURE`): When this bit is set, the requested _route_ was marked _secure_ but an _unlock sequence_ has not completed.
* Bit 0 (`SUCCESS`): When this bit is set, the request was successfully handled. If not set, all payload data should be disregarded, and the request retried if appropriate (with a new token).
### Example "conversation":
**Request** -- version query:
| Byte | 0 | 1 | 2 | 3 | 4 |
| --- | --- | --- | --- | --- | --- |
| **Purpose** | Token | Token | Payload Length | Route | Route |
| **Value** | `0x43` | `0x2B` | `0x02` | `0x00` | `0x00` |
**Response** -- matching token, successful flag, payload of `0x03170192` = 3.17.192:
| Byte | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| **Purpose** | Token | Token | Response Flags | Payload Length | Payload | Payload | Payload | Payload |
| **Value** | `0x43` | `0x2B` | `0x01` | `0x04` | `0x92` | `0x01` | `0x17` | `0x03` |
## Routes
Subsystem validity should be queried through the “Enabled-in-firmware subsystem query” under the QMK subsystem (route=0x00,0x01).
This is the primary method for determining if a subsystem has been enabled in the running firmware.
### XAP - `0x00`
This subsystem is always present, and provides the ability to query information about the XAP protocol of the connected device.
| Name | Route | Tags | Payloads | Description |
| -- | -- | -- | -- | -- |
| Version Query | `0x00 0x00` | |__Response:__ `u32`| XAP protocol version query.<br><br>* Returns the BCD-encoded version in the format of XX.YY.ZZZZ => `0xXXYYZZZZ`<br> * e.g. 3.2.115 will match `0x03020115`, or bytes {0x15,0x01,0x02,0x03}.|
| Capabilities Query | `0x00 0x01` | |__Response:__ `u32`| XAP subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.|
| Enabled subsystem query | `0x00 0x02` | |__Response:__ `u32`| XAP protocol subsystem query. Each bit should be considered as a "usable" subsystem. For example, checking `(value & (1 << XAP_ROUTE_QMK) != 0)` means the QMK subsystem is enabled and available for querying.|
| Secure Status | `0x00 0x03` | |__Response:__ `u8`| Query secure route status<br><br>* 0 means secure routes are disabled<br>* 1 means unlock sequence initiated but incomplete<br>* 2 means secure routes are allowed<br>* any other value should be interpreted as disabled|
| Secure Unlock | `0x00 0x04` | || Initiate secure route unlock sequence|
| Secure Lock | `0x00 0x05` | || Disable secure routes|
### QMK - `0x01`
This subsystem is always present, and provides the ability to address QMK-specific functionality.
| Name | Route | Tags | Payloads | Description |
| -- | -- | -- | -- | -- |
| Version Query | `0x01 0x00` | |__Response:__ `u32`| QMK protocol version query.<br><br>* Returns the BCD-encoded version in the format of XX.YY.ZZZZ => `0xXXYYZZZZ`<br> * e.g. 3.2.115 will match `0x03020115`, or bytes {0x15,0x01,0x02,0x03}.|
| Capabilities Query | `0x01 0x01` | |__Response:__ `u32`| QMK subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.|
| Board identifiers | `0x01 0x02` | |__Response:__<br>&nbsp;&nbsp;&nbsp;&nbsp;* Vendor ID: `u16`<br>&nbsp;&nbsp;&nbsp;&nbsp;* Product ID: `u16`<br>&nbsp;&nbsp;&nbsp;&nbsp;* Product Version: `u16`<br>&nbsp;&nbsp;&nbsp;&nbsp;* QMK Unique Identifier: `u32`| Retrieves the set of identifying information for the board.|
| Board Manufacturer | `0x01 0x03` | |__Response:__ `string`| Retrieves the name of the manufacturer|
| Product Name | `0x01 0x04` | |__Response:__ `string`| Retrieves the product name|
| Config Blob Length | `0x01 0x05` | |__Response:__ `u32`| Retrieves the length of the configuration data bundled within the firmware|
| Config Blob Chunk | `0x01 0x06` | |__Request:__ `u16`<br><br>__Response:__ `u8[32]`| Retrieves a chunk of the configuration data bundled within the firmware|
| Jump to bootloader | `0x01 0x07` | __Secure__ |__Response:__ `u8`| Jump to bootloader<br><br>May not be present if QMK capabilities query returns “true”, then jump to bootloader is supported<br><br>* 0 means secure routes are disabled, and should be considered as a failure<br>* 1 means successful, board will jump to bootloader|
| Hardware Identifier | `0x01 0x08` | |__Response:__ `u32[4]`| Retrieves a unique identifier for the board.|
### Keyboard - `0x02`
This subsystem is always present, and reserved for vendor-specific functionality. No routes are defined by XAP.
### User - `0x03`
This subsystem is always present, and reserved for user-specific functionality. No routes are defined by XAP.
## Broadcast messages
Broadcast messages may be sent by the firmware to the host, without a corresponding inbound request. Each broadcast message uses the token `0xFFFF`, and does not expect a response from the host. Tokens are followed by an _ID_ signifying the type of broadcast, with corresponding _payload_.
### Log message - `0x00`
Replicates and replaces the same functionality as if using the standard QMK `CONSOLE_ENABLE = yes` in `rules.mk`. Normal prints within the firmware will manifest as log messages broadcast to the host. `hid_listen` will not be functional with XAP enabled.
Log message payloads include a `u8` signifying the length of the text, followed by the `u8[Length]` containing the text itself.
**Example Log Broadcast** -- log message "Hello QMK!"
| Byte | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| **Purpose** | Token | Token | Broadcast Type | Length | Payload | Payload | Payload | Payload | Payload | Payload | Payload | Payload | Payload | Payload |
| **Value** | `0xFF` | `0xFF` | `0x00` | `0x0A`(10) | `0x48`(H) | `0x65`(e) | `0x6C`(l) | `0x6C`(l) | `0x6F`(o) | `0x20`(&nbsp;) | `0x51`(Q) | `0x4D`(M) | `0x4B`(K) | `0x21`(!) |
### Secure Status - `0x01`
Secure status has changed. Payloads include a `u8` matching a 'Secure Status' request.
**Example Secure Status Broadcast** -- secure "Unlocking"
| Byte | 0 | 1 | 2 | 3 |
| --- | --- | --- | --- | --- |
| **Purpose** | Token | Token | Broadcast Type | Secure Status |
| **Value** | `0xFF` | `0xFF` | `0x01` | `0x01` |
### Keyboard - `0x02`
Reserved for vendor-specific functionality. No messages are defined by XAP.
### User - `0x03`
Reserved for user-specific functionality. No messages are defined by XAP.

187
docs/xap_0.2.0.md Normal file
View file

@ -0,0 +1,187 @@
# QMK Firmware XAP Specs
This document describes the requirements of the QMK XAP ("extensible application protocol") API.
## Types
**All integral types are little-endian.**
| Name | Definition |
| -- | -- |
| _struct{}_ | A structure of data, packing different objects together. Data is "compacted" -- there are no padding bytes between fields. Equivalent to a packed C-style `struct`. The order in which they're defined matches the order of the data in the response packet. |
| _type[n]_ | An array of `type`, with array extent of `N` -- e.g. `u8[2]` signifies two consecutive octets. |
| _u16_ | An unsigned 16-bit integral, commonly seen as `uint16_t` from _stdint.h_. |
| _u32_ | An unsigned 32-bit integral, commonly seen as `uint32_t` from _stdint.h_. |
| _u64_ | An unsigned 64-bit integral, commonly seen as `uint64_t` from _stdint.h_. |
| _u8_ | An unsigned 8-bit integral (octet, or byte), commonly seen as `uint8_t` from _stdint.h_. |
## Definitions
This list defines the terms used across the entire set of XAP protocol documentation.
| Name | Definition |
| -- | -- |
| _Capability_ | A way to determine if certain functionality is enabled in the firmware. Any _subsystem_ that provides build-time restriction of functionality must provide a _route_ for a _capabilities query_. |
| _Handler_ | A piece of code that is executed when a specific _route_ is received. |
| _Payload_ | Any received data appended to the _route_, which gets delivered to the _handler_ when received. |
| _Response_ | The data sent back to the host during execution of a _handler_. |
| _Route_ | A sequence of _IDs_ describing the route to invoke a _handler_. |
| _Secure Route_ | A _route_ which has potentially destructive consequences, necessitating prior approval by the user before executing. |
| _Subsystem_ | A high-level area of functionality within XAP. |
| _Unlock sequence_ | A physical sequence initiated by the user to enable execution of _secure routes_. |
| _Broadcast Header_ | Packet format for broadcast messages. Takes the format:<br>`token` - token<br>`u8` - type<br>`u8` - length |
| _ID_ | A single octet / 8-bit byte, representing Subsystem or Route index. |
| _Request Header_ | Packet format for inbound data. Takes the format:<br>`token` - token<br>`u8` - length |
| _Response Flags_ | An `u8` containing the status of the request. |
| _Response Header_ | Packet format for outbound data. Takes the format:<br>`token` - token<br>`response_flags` - flags<br>`u8` - length |
| _Token_ | A `u16` associated with a specific request as well as its corresponding response. Valid token values are within the range `0x0100`-`0xFFFF`. |
## Requests and Responses
Communication generally follows a request/response pattern.
Each request needs to include a _token_ -- this `u16` value prefixes each outbound request from the host application and its corresponding response.
This allows response messages to be correlated with their request, even if multiple host applications are communicating with the firmware simultaneously.
Host applications should randomly generate a token ID for **every** outbound request, unless using a reserved token defined below.
To ensure host interoperability, valid token values are within the range `0x0100`-`0xFFFF`.
This token is followed by a `u8` signifying the length of data in the request.
Two token values are reserved: `0xFFFE` and `0xFFFF`:
* `0xFFFE`: A message sent by a host application may use this token if no response is to be sent -- a "fire and forget" message.
* `0xFFFF`: Signifies a "broadcast" message sent by the firmware without prompting from the host application. Broadcast messages are defined later in this document.
Any request will generate at least one corresponding response, with the exception of messages using reserved tokens. Maximum total message length is 128 bytes due to RAM constraints.
Response messages will always be prefixed by the originating request _token_, directly followed by that request's _response flags_, then the response payload length:
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
| -- | -- | -- | -- | -- | -- | -- | -- |
| `UNLOCKED` | `UNLOCK_IN_PROGRESS` | `-` | `-` | `-` | `-` | `SECURE_FAILURE` | `SUCCESS` |
* Bit 7 (`UNLOCKED`): When this bit is set, an _unlock sequence_ has completed, and _secure routes_ may be invoked.
* Bit 6 (`UNLOCK_IN_PROGRESS`): When this bit is set, an _unlock sequence_ is in progress.
* Bit 1 (`SECURE_FAILURE`): When this bit is set, the requested _route_ was marked _secure_ but an _unlock sequence_ has not completed.
* Bit 0 (`SUCCESS`): When this bit is set, the request was successfully handled. If not set, all payload data should be disregarded, and the request retried if appropriate (with a new token).
### Example "conversation":
**Request** -- version query:
| Byte | 0 | 1 | 2 | 3 | 4 |
| --- | --- | --- | --- | --- | --- |
| **Purpose** | Token | Token | Payload Length | Route | Route |
| **Value** | `0x43` | `0x2B` | `0x02` | `0x00` | `0x00` |
**Response** -- matching token, successful flag, payload of `0x03170192` = 3.17.192:
| Byte | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| **Purpose** | Token | Token | Response Flags | Payload Length | Payload | Payload | Payload | Payload |
| **Value** | `0x43` | `0x2B` | `0x01` | `0x04` | `0x92` | `0x01` | `0x17` | `0x03` |
## Routes
Subsystem validity should be queried through the “Enabled-in-firmware subsystem query” under the QMK subsystem (route=0x00,0x01).
This is the primary method for determining if a subsystem has been enabled in the running firmware.
### XAP - `0x00`
This subsystem is always present, and provides the ability to query information about the XAP protocol of the connected device.
| Name | Route | Tags | Payloads | Description |
| -- | -- | -- | -- | -- |
| Version Query | `0x00 0x00` | |__Response:__ `u32`| XAP protocol version query.<br><br>* Returns the BCD-encoded version in the format of XX.YY.ZZZZ => `0xXXYYZZZZ`<br> * e.g. 3.2.115 will match `0x03020115`, or bytes {0x15,0x01,0x02,0x03}.|
| Capabilities Query | `0x00 0x01` | |__Response:__ `u32`| XAP subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.|
| Enabled subsystem query | `0x00 0x02` | |__Response:__ `u32`| XAP protocol subsystem query. Each bit should be considered as a "usable" subsystem. For example, checking `(value & (1 << XAP_ROUTE_QMK) != 0)` means the QMK subsystem is enabled and available for querying.|
| Secure Status | `0x00 0x03` | |__Response:__ `u8`| Query secure route status<br><br>* 0 means secure routes are disabled<br>* 1 means unlock sequence initiated but incomplete<br>* 2 means secure routes are allowed<br>* any other value should be interpreted as disabled|
| Secure Unlock | `0x00 0x04` | || Initiate secure route unlock sequence|
| Secure Lock | `0x00 0x05` | || Disable secure routes|
### QMK - `0x01`
This subsystem is always present, and provides the ability to address QMK-specific functionality.
| Name | Route | Tags | Payloads | Description |
| -- | -- | -- | -- | -- |
| Version Query | `0x01 0x00` | |__Response:__ `u32`| QMK protocol version query.<br><br>* Returns the BCD-encoded version in the format of XX.YY.ZZZZ => `0xXXYYZZZZ`<br> * e.g. 3.2.115 will match `0x03020115`, or bytes {0x15,0x01,0x02,0x03}.|
| Capabilities Query | `0x01 0x01` | |__Response:__ `u32`| QMK subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.|
| Board identifiers | `0x01 0x02` | |__Response:__<br>&nbsp;&nbsp;&nbsp;&nbsp;* Vendor ID: `u16`<br>&nbsp;&nbsp;&nbsp;&nbsp;* Product ID: `u16`<br>&nbsp;&nbsp;&nbsp;&nbsp;* Product Version: `u16`<br>&nbsp;&nbsp;&nbsp;&nbsp;* QMK Unique Identifier: `u32`| Retrieves the set of identifying information for the board.|
| Board Manufacturer | `0x01 0x03` | |__Response:__ `string`| Retrieves the name of the manufacturer|
| Product Name | `0x01 0x04` | |__Response:__ `string`| Retrieves the product name|
| Config Blob Length | `0x01 0x05` | |__Response:__ `u32`| Retrieves the length of the configuration data bundled within the firmware|
| Config Blob Chunk | `0x01 0x06` | |__Request:__ `u16`<br><br>__Response:__ `u8[32]`| Retrieves a chunk of the configuration data bundled within the firmware|
| Jump to bootloader | `0x01 0x07` | __Secure__ |__Response:__ `u8`| Jump to bootloader<br><br>May not be present if QMK capabilities query returns “true”, then jump to bootloader is supported<br><br>* 0 means secure routes are disabled, and should be considered as a failure<br>* 1 means successful, board will jump to bootloader|
| Hardware Identifier | `0x01 0x08` | |__Response:__ `u32[4]`| Retrieves a unique identifier for the board.|
### Keyboard - `0x02`
This subsystem is always present, and reserved for vendor-specific functionality. No routes are defined by XAP.
### User - `0x03`
This subsystem is always present, and reserved for user-specific functionality. No routes are defined by XAP.
### Keymap - `0x04`
This subsystem allows for query of currently configured keycodes.
| Name | Route | Tags | Payloads | Description |
| -- | -- | -- | -- | -- |
| Capabilities Query | `0x04 0x01` | |__Response:__ `u32`| Keymap subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.|
| Get Layer Count | `0x04 0x02` | |__Response:__ `u8`| Query maximum number of layers that can be addressed within the keymap.|
| Get Keycode | `0x04 0x03` | |__Request:__<br>&nbsp;&nbsp;&nbsp;&nbsp;* Layer: `u8`<br>&nbsp;&nbsp;&nbsp;&nbsp;* Row: `u8`<br>&nbsp;&nbsp;&nbsp;&nbsp;* Column: `u8`<br><br>__Response:__ `u16`| Query the Keycode at the requested location.|
| Get Encoder Keycode | `0x04 0x04` | |__Request:__<br>&nbsp;&nbsp;&nbsp;&nbsp;* Layer: `u8`<br>&nbsp;&nbsp;&nbsp;&nbsp;* Encoder: `u8`<br>&nbsp;&nbsp;&nbsp;&nbsp;* Clockwise: `u8`<br><br>__Response:__ `u16`| Query the Keycode at the requested location.|
### Remapping - `0x05`
This subsystem allows for live reassignment of keycodes without rebuilding the firmware.
| Name | Route | Tags | Payloads | Description |
| -- | -- | -- | -- | -- |
| Capabilities Query | `0x05 0x01` | |__Response:__ `u32`| Remapping subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.|
| Get Layer Count | `0x05 0x02` | |__Response:__ `u8`| Query maximum number of layers that can be addressed within the keymap.|
| Set Keycode | `0x05 0x03` | __Secure__ |__Request:__<br>&nbsp;&nbsp;&nbsp;&nbsp;* Layer: `u8`<br>&nbsp;&nbsp;&nbsp;&nbsp;* Row: `u8`<br>&nbsp;&nbsp;&nbsp;&nbsp;* Column: `u8`<br>&nbsp;&nbsp;&nbsp;&nbsp;* Keycode: `u16`| Modify the Keycode at the requested location.|
| Set Encoder Keycode | `0x05 0x04` | __Secure__ |__Request:__<br>&nbsp;&nbsp;&nbsp;&nbsp;* Layer: `u8`<br>&nbsp;&nbsp;&nbsp;&nbsp;* Encoder: `u8`<br>&nbsp;&nbsp;&nbsp;&nbsp;* Clockwise: `u8`<br>&nbsp;&nbsp;&nbsp;&nbsp;* Keycode: `u16`| Modify the Keycode at the requested location.|
### Lighting - `0x06`
This subsystem allows for control over the lighting subsystem.
| Name | Route | Tags | Payloads | Description |
| -- | -- | -- | -- | -- |
| Capabilities Query | `0x06 0x01` | |__Response:__ `u32`| Lighting subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.|
## Broadcast messages
Broadcast messages may be sent by the firmware to the host, without a corresponding inbound request. Each broadcast message uses the token `0xFFFF`, and does not expect a response from the host. Tokens are followed by an _ID_ signifying the type of broadcast, with corresponding _payload_.
### Log message - `0x00`
Replicates and replaces the same functionality as if using the standard QMK `CONSOLE_ENABLE = yes` in `rules.mk`. Normal prints within the firmware will manifest as log messages broadcast to the host. `hid_listen` will not be functional with XAP enabled.
Log message payloads include a `u8` signifying the length of the text, followed by the `u8[Length]` containing the text itself.
**Example Log Broadcast** -- log message "Hello QMK!"
| Byte | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| **Purpose** | Token | Token | Broadcast Type | Length | Payload | Payload | Payload | Payload | Payload | Payload | Payload | Payload | Payload | Payload |
| **Value** | `0xFF` | `0xFF` | `0x00` | `0x0A`(10) | `0x48`(H) | `0x65`(e) | `0x6C`(l) | `0x6C`(l) | `0x6F`(o) | `0x20`(&nbsp;) | `0x51`(Q) | `0x4D`(M) | `0x4B`(K) | `0x21`(!) |
### Secure Status - `0x01`
Secure status has changed. Payloads include a `u8` matching a 'Secure Status' request.
**Example Secure Status Broadcast** -- secure "Unlocking"
| Byte | 0 | 1 | 2 | 3 |
| --- | --- | --- | --- | --- |
| **Purpose** | Token | Token | Broadcast Type | Secure Status |
| **Value** | `0xFF` | `0xFF` | `0x01` | `0x01` |
### Keyboard - `0x02`
Reserved for vendor-specific functionality. No messages are defined by XAP.
### User - `0x03`
Reserved for user-specific functionality. No messages are defined by XAP.

4
docs/xap_protocol.md Normal file
View file

@ -0,0 +1,4 @@
<!-- This file is generated -->
* [XAP Version 0.2.0](xap_0.2.0.md)
* [XAP Version 0.1.0](xap_0.1.0.md)
* [XAP Version 0.0.1](xap_0.0.1.md)

View file

@ -0,0 +1,102 @@
// Copyright 2020 zvecr <git@zvecr.com>
// SPDX-License-Identifier: GPL-2.0-or-later
#include QMK_KEYBOARD_H
// Defines names for use in layer keycodes and the keymap
enum layer_names {
_QWERTY,
_LOWER,
_RAISE,
_ADJUST,
};
#define LOWER MO(_LOWER)
#define RAISE MO(_RAISE)
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
/* Qwerty
* ,-----------------------------------------------------------------------------------.
* | Esc | Q | W | E | R | T | Y | U | I | O | P | Bksp |
* |------+------+------+------+------+-------------+------+------+------+------+------|
* | Tab | A | S | D | F | G | H | J | K | L | ; | " |
* |------+------+------+------+------+------|------+------+------+------+------+------|
* | Shift| Z | X | C | V | B | N | M | , | . | / |Enter |
* |------+------+------+------+------+------+------+------+------+------+------+------|
* | Ctrl | GUI | Alt | App |Lower | Space |Raise | Left | Down | Up |Right |
* `-----------------------------------------------------------------------------------'
*/
[_QWERTY] = LAYOUT_ortho_4x12(
KC_ESC, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_BSPC,
KC_TAB, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT,
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_ENT ,
KC_LCTL, KC_LGUI, KC_LALT, KC_APP, LOWER, KC_SPC, KC_SPC, RAISE, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT
),
/* Lower
* ,-----------------------------------------------------------------------------------.
* | ~ | ! | @ | # | $ | % | ^ | & | * | ( | ) | Del |
* |------+------+------+------+------+-------------+------+------+------+------+------|
* | Del | F1 | F2 | F3 | F4 | F5 | F6 | _ | + | { | } | | |
* |------+------+------+------+------+------|------+------+------+------+------+------|
* | | F7 | F8 | F9 | F10 | F11 | F12 |ISO ~ |ISO | | | | |
* |------+------+------+------+------+------+------+------+------+------+------+------|
* | | | | | | | | | Next | Vol- | Vol+ | Play |
* `-----------------------------------------------------------------------------------'
*/
[_LOWER] = LAYOUT_ortho_4x12(
KC_TILD, KC_EXLM, KC_AT, KC_HASH, KC_DLR, KC_PERC, KC_CIRC, KC_AMPR, KC_ASTR, KC_LPRN, KC_RPRN, KC_DEL,
KC_DEL, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_UNDS, KC_PLUS, KC_LCBR, KC_RCBR, KC_PIPE,
_______, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12,S(KC_NUHS),S(KC_NUBS),_______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, KC_MNXT, KC_VOLD, KC_VOLU, KC_MPLY
),
/* Raise
* ,-----------------------------------------------------------------------------------.
* | ` | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | Del |
* |------+------+------+------+------+-------------+------+------+------+------+------|
* | Del | F1 | F2 | F3 | F4 | F5 | F6 | - | = | [ | ] | \ |
* |------+------+------+------+------+------|------+------+------+------+------+------|
* | | F7 | F8 | F9 | F10 | F11 | F12 |ISO # |ISO / | | | |
* |------+------+------+------+------+------+------+------+------+------+------+------|
* | | | | | | | | | Next | Vol- | Vol+ | Play |
* `-----------------------------------------------------------------------------------'
*/
[_RAISE] = LAYOUT_ortho_4x12(
KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_DEL,
KC_DEL, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_MINS, KC_EQL, KC_LBRC, KC_RBRC, KC_BSLS,
_______, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_NUHS, KC_NUBS, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, KC_MNXT, KC_VOLD, KC_VOLU, KC_MPLY
),
/* Adjust (Lower + Raise)
* ,-----------------------------------------------------------------------------------.
* | | Reset| | | | |R Tog |R Mode|R Rev |R Grad| Reset| |
* |------+------+------+------+------+-------------+------+------+------+------+------|
* | | | | | | |R HUI|R SAI|R VAI| | | |
* |------+------+------+------+------+------|------+------+------+------+------+------|
* | | | | | | |R HUD|R SAD|R VAD| | | |
* |------+------+------+------+------+------+------+------+------+------+------+------|
* | | | | | | | | | | | | |
* `-----------------------------------------------------------------------------------'
*/
[_ADJUST] = LAYOUT_ortho_4x12(
_______, RESET, _______, _______, _______, _______, RGB_TOG, RGB_MOD, RGB_RMOD,RGB_M_G, RESET, _______,
_______, _______, _______, _______, _______, _______, RGB_HUI, RGB_SAI, RGB_VAI, _______, _______, _______,
_______, _______, _______, _______, _______, _______, RGB_HUD, RGB_SAD, RGB_VAD, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______
)
};
layer_state_t layer_state_set_user(layer_state_t state) {
return update_tri_layer_state(state, _LOWER, _RAISE, _ADJUST);
}
#if defined(ENCODER_MAP_ENABLE)
const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][2] = {
[_QWERTY] = { ENCODER_CCW_CW(KC_MS_WH_UP, KC_MS_WH_DOWN), ENCODER_CCW_CW(KC_VOLD, KC_VOLU) },
[_LOWER] = { ENCODER_CCW_CW(RGB_HUD, RGB_HUI), ENCODER_CCW_CW(RGB_SAD, RGB_SAI) },
[_RAISE] = { ENCODER_CCW_CW(RGB_VAD, RGB_VAI), ENCODER_CCW_CW(RGB_SPD, RGB_SPI) },
[_ADJUST] = { ENCODER_CCW_CW(RGB_RMOD, RGB_MOD), ENCODER_CCW_CW(KC_RIGHT, KC_LEFT) },
};
#endif

View file

@ -0,0 +1,2 @@
ENCODER_MAP_ENABLE = yes
XAP_ENABLE = yes

View file

@ -0,0 +1,16 @@
{
routes: {
0x01: {
type: command
name: Capabilities Query
define: CAPABILITIES_QUERY_USER
description:
'''
USER subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.
'''
return_type: u32
return_purpose: capabilities
return_constant: XAP_ROUTE_USER_CAPABILITIES
}
}
}

View file

@ -0,0 +1,16 @@
{
routes: {
0x01: {
type: command
name: Capabilities Query
define: CAPABILITIES_QUERY_KB
description:
'''
KB subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.
'''
return_type: u32
return_purpose: capabilities
return_constant: XAP_ROUTE_KB_CAPABILITIES
}
}
}

65
lib/python/qmk/casing.py Executable file
View file

@ -0,0 +1,65 @@
"""This script handles conversion between snake and camel casing.
"""
import re
_words_expr = re.compile(r"([a-zA-Z][^A-Z0-9]*|[0-9]+)")
_lower_snake_case_expr = re.compile(r'^[a-z][a-z0-9_]*$')
_upper_snake_case_expr = re.compile(r'^[A-Z][A-Z0-9_]*$')
def _is_snake_case(str):
"""Checks if the supplied string is already in snake case.
"""
match = _lower_snake_case_expr.match(str)
if match:
return True
match = _upper_snake_case_expr.match(str)
if match:
return True
return False
def _split_snake_case(str):
"""Splits up a string based on underscores, if it's in snake casing.
"""
if _is_snake_case(str):
return [s.lower() for s in str.split("_")]
return str
def _split_camel_case(str):
"""Splits up a string based on capitalised camel casing.
"""
return _words_expr.findall(str)
def _split_cased_words(str):
return _split_snake_case(str) if _is_snake_case(str) else _split_camel_case(str)
def to_snake(str):
str = "_".join([word.strip().lower() for word in _split_cased_words(str)])
# Fix acronyms
str = str.replace('i_d', 'id')
str = str.replace('x_a_p', 'xap')
str = str.replace('q_m_k', 'qmk')
return str
def to_upper_snake(str):
return to_snake(str).upper()
def to_camel(str):
def _acronym(w):
if w.strip().lower() == 'qmk':
return 'QMK'
elif w.strip().lower() == 'xap':
return 'XAP'
elif w.strip().lower() == 'id':
return 'ID'
return w.title()
return "".join([_acronym(word) for word in _split_cased_words(str)])

View file

@ -17,7 +17,8 @@ import_names = {
'pep8-naming': 'pep8ext_naming',
'pyusb': 'usb.core',
'qmk-dotty-dict': 'dotty_dict',
'pillow': 'PIL'
'pillow': 'PIL',
'Jinja2': 'jinja2'
}
safe_commands = [
@ -54,6 +55,7 @@ subcommands = [
'qmk.cli.generate.info_json',
'qmk.cli.generate.keyboard_c',
'qmk.cli.generate.keyboard_h',
'qmk.cli.generate.keymap_hash',
'qmk.cli.generate.layouts',
'qmk.cli.generate.rgb_breathe_table',
'qmk.cli.generate.rules_mk',
@ -76,6 +78,10 @@ subcommands = [
'qmk.cli.pyformat',
'qmk.cli.pytest',
'qmk.cli.via2json',
'qmk.cli.xap',
'qmk.cli.xap.generate_docs',
'qmk.cli.xap.generate_json',
'qmk.cli.xap.generate_qmk',
]

View file

@ -1,24 +1,17 @@
"""Ensure text files have the proper line endings.
"""
from itertools import islice
from subprocess import DEVNULL
from milc import cli
from qmk.path import normpath
def _get_chunks(it, size):
"""Break down a collection into smaller parts
"""
it = iter(it)
return iter(lambda: tuple(islice(it, size)), ())
from qmk.commands import get_chunks
def dos2unix_run(files):
"""Spawn multiple dos2unix subprocess avoiding too long commands on formatting everything
"""
for chunk in _get_chunks(files, 10):
for chunk in get_chunks(files, 10):
dos2unix = cli.run(['dos2unix', *chunk])
if dos2unix.returncode:

View file

@ -2,6 +2,7 @@
"""
from pathlib import Path
import shutil
import hjson
import json
from milc import cli
@ -11,30 +12,16 @@ from qmk.info import info_json
from qmk.json_encoders import InfoJSONEncoder
from qmk.json_schema import json_load
from qmk.keyboard import find_readme, list_keyboards
from qmk.xap.common import get_xap_definition_files, update_xap_definitions
TEMPLATE_PATH = Path('data/templates/api/')
DATA_PATH = Path('data')
TEMPLATE_PATH = DATA_PATH / 'templates/api/'
BUILD_API_PATH = Path('.build/api_data/')
@cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help="Don't write the data to disk.")
@cli.argument('-f', '--filter', arg_only=True, action='append', default=[], help="Filter the list of keyboards based on partial name matches the supplied value. May be passed multiple times.")
@cli.subcommand('Creates a new keymap for the keyboard of your choosing', hidden=False if cli.config.user.developer else True)
def generate_api(cli):
"""Generates the QMK API data.
def _filtered_keyboard_list():
"""Perform basic filtering of list_keyboards
"""
if BUILD_API_PATH.exists():
shutil.rmtree(BUILD_API_PATH)
shutil.copytree(TEMPLATE_PATH, BUILD_API_PATH)
v1_dir = BUILD_API_PATH / 'v1'
keyboard_all_file = v1_dir / 'keyboards.json' # A massive JSON containing everything
keyboard_list_file = v1_dir / 'keyboard_list.json' # A simple list of keyboard targets
keyboard_aliases_file = v1_dir / 'keyboard_aliases.json' # A list of historical keyboard names and their new name
keyboard_metadata_file = v1_dir / 'keyboard_metadata.json' # All the data configurator/via needs for initialization
usb_file = v1_dir / 'usb.json' # A mapping of USB VID/PID -> keyboard target
# Filter down when required
keyboard_list = list_keyboards()
if cli.args.filter:
kb_list = []
@ -42,6 +29,46 @@ def generate_api(cli):
if any(i in keyboard_name for i in cli.args.filter):
kb_list.append(keyboard_name)
keyboard_list = kb_list
return keyboard_list
def _resolve_xap_specs(output_folder):
"""To make it easier for consumers, publish pre-merged spec files
"""
overall = None
for file in get_xap_definition_files():
overall = update_xap_definitions(overall, hjson.load(file.open(encoding='utf-8')))
# Inject dummy bits for unspecified response flags
for n in range(0, 8):
if str(n) not in overall['response_flags']['bits']:
overall['response_flags']['bits'][str(n)] = {'name': '', 'description': '', 'define': '-'}
output_file = output_folder / (file.stem + ".json")
output_file.write_text(json.dumps(overall, indent=4), encoding='utf-8')
@cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help="Don't write the data to disk.")
@cli.argument('-f', '--filter', arg_only=True, action='append', default=[], help="Filter the list of keyboards based on partial name matches the supplied value. May be passed multiple times.")
@cli.subcommand('Generate QMK API data', hidden=False if cli.config.user.developer else True)
def generate_api(cli):
"""Generates the QMK API data.
"""
v1_dir = BUILD_API_PATH / 'v1'
keyboard_all_file = v1_dir / 'keyboards.json' # A massive JSON containing everything
keyboard_list_file = v1_dir / 'keyboard_list.json' # A simple list of keyboard targets
keyboard_aliases_file = v1_dir / 'keyboard_aliases.json' # A list of historical keyboard names and their new name
keyboard_metadata_file = v1_dir / 'keyboard_metadata.json' # All the data configurator/via needs for initialization
usb_file = v1_dir / 'usb.json' # A mapping of USB VID/PID -> keyboard target
if BUILD_API_PATH.exists():
shutil.rmtree(BUILD_API_PATH)
shutil.copytree(TEMPLATE_PATH, BUILD_API_PATH)
shutil.copytree(DATA_PATH, v1_dir)
# Filter down when required
keyboard_list = _filtered_keyboard_list()
kb_all = {}
usb_list = {}
@ -86,6 +113,9 @@ def generate_api(cli):
'usb': usb_list,
}
# Feature specific handling
_resolve_xap_specs(v1_dir / 'xap')
# Write the global JSON files
keyboard_all_json = json.dumps({'last_updated': current_datetime(), 'keyboards': kb_all}, cls=InfoJSONEncoder)
usb_json = json.dumps({'last_updated': current_datetime(), 'usb': usb_list}, cls=InfoJSONEncoder)

View file

@ -0,0 +1,36 @@
"""Used by the make system to generate header.
"""
from fnvhash import fnv1a_32
from milc import cli
from qmk.commands import dump_lines
from qmk.keymap import locate_keymap
from qmk.keyboard import keyboard_completer, keyboard_folder
from qmk.path import normpath
from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE
@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')
@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, required=True, help='Keyboard to generate header for.')
@cli.argument('-km', '--keymap', arg_only=True, required=True, help='Keymap to generate header for.')
@cli.subcommand('Used by the make system to generate header', hidden=True)
def generate_keymap_hash(cli):
# Build the header file.
header_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once']
keymap_folder = locate_keymap(cli.args.keyboard, cli.args.keymap).parent
keymap_files = list(keymap_folder.glob('**/*'))
keymap_files.sort()
content = ""
for file in keymap_files:
content += file.read_text(encoding='utf-8')
val = fnv1a_32(bytes(content, 'utf-8'))
header_lines.append(f'#define KEYMAP_HASH 0x{val:08X}ul')
# Show the results
dump_lines(cli.args.output, header_lines, cli.args.quiet)

View file

@ -0,0 +1 @@
from .xap import xap

View file

@ -0,0 +1,38 @@
"""This script generates the XAP protocol documentation.
"""
import hjson
from qmk.constants import QMK_FIRMWARE
from qmk.xap.common import get_xap_definition_files, update_xap_definitions, render_xap_output
from milc import cli
@cli.subcommand('Generates the XAP protocol documentation.', hidden=False if cli.config.user.developer else True)
def xap_generate_docs(cli):
"""Generates the XAP protocol documentation by merging the definitions files, and producing the corresponding Markdown document under `/docs/`.
"""
docs_list = []
overall = None
for file in get_xap_definition_files():
overall = update_xap_definitions(overall, hjson.load(file.open(encoding='utf-8')))
# Inject dummy bits for unspecified response flags
for n in range(0, 8):
if str(n) not in overall['response_flags']['bits']:
overall['response_flags']['bits'][str(n)] = {'name': '', 'description': '', 'define': '-'}
output_doc = QMK_FIRMWARE / "docs" / f"{file.stem}.md"
docs_list.append(output_doc)
output = render_xap_output('docs', 'docs.md.j2', overall)
with open(output_doc, "w", encoding='utf-8') as out_file:
out_file.write(output)
output_doc = QMK_FIRMWARE / "docs" / "xap_protocol.md"
with open(output_doc, "w", encoding='utf-8') as out_file:
out_file.write('''\
<!-- This file is generated -->
''')
for file in reversed(sorted(docs_list)):
ver = file.stem[4:]
out_file.write(f'* [XAP Version {ver}]({file.name})\n')

View file

@ -0,0 +1,13 @@
"""This script generates the consolidated XAP protocol definitions.
"""
import hjson
from milc import cli
from qmk.xap.common import latest_xap_defs
@cli.subcommand('Generates the consolidated XAP protocol definitions.', hidden=False if cli.config.user.developer else True)
def xap_generate_json(cli):
"""Generates the consolidated XAP protocol definitions.
"""
defs = latest_xap_defs()
print(hjson.dumps(defs))

View file

@ -0,0 +1,69 @@
"""This script generates the XAP protocol generated sources to be compiled into QMK firmware.
"""
from milc import cli
from qmk.path import normpath
from qmk.keyboard import keyboard_completer, keyboard_folder
from qmk.xap.gen_firmware.blob_generator import generate_blob
from qmk.xap.gen_firmware.inline_generator import generate_inline
from qmk.xap.gen_firmware.header_generator import generate_header
@cli.argument('-o', '--output', type=normpath, help='File to write to')
@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Name of the keyboard')
@cli.argument('-km', '--keymap', help='The keymap\'s name')
@cli.subcommand('Generates the XAP protocol include.', hidden=False if cli.config.user.developer else True)
def xap_generate_qmk_inc(cli):
"""Generates the XAP protocol inline codegen file, generated during normal build.
"""
# Determine our keyboard/keymap
if not cli.args.keyboard:
cli.log.error('Missing parameter: --keyboard')
cli.subcommands['xap-generate-info-h'].print_help()
return False
if not cli.args.keymap:
cli.log.error('Missing parameter: --keymap')
cli.subcommands['xap-generate-info-h'].print_help()
return False
generate_inline(cli.args.output, cli.args.keyboard, cli.args.keymap)
@cli.argument('-o', '--output', type=normpath, help='File to write to')
@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Name of the keyboard')
@cli.argument('-km', '--keymap', help='The keymap\'s name')
@cli.subcommand('Generates the XAP protocol include.', hidden=False if cli.config.user.developer else True)
def xap_generate_qmk_h(cli):
"""Generates the XAP protocol header file, generated during normal build.
"""
# Determine our keyboard/keymap
if not cli.args.keyboard:
cli.log.error('Missing parameter: --keyboard')
cli.subcommands['xap-generate-info-h'].print_help()
return False
if not cli.args.keymap:
cli.log.error('Missing parameter: --keymap')
cli.subcommands['xap-generate-info-h'].print_help()
return False
generate_header(cli.args.output, cli.args.keyboard, cli.args.keymap)
@cli.argument('-o', '--output', type=normpath, help='File to write to')
@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Name of the keyboard')
@cli.argument('-km', '--keymap', help='The keymap\'s name')
@cli.subcommand('Generates the XAP config payload include.', hidden=False if cli.config.user.developer else True)
def xap_generate_qmk_blob_h(cli):
"""Generates the XAP config payload header file, generated during normal build.
"""
# Determine our keyboard/keymap
if not cli.args.keyboard:
cli.log.error('Missing parameter: --keyboard')
cli.subcommands['xap-generate-info-h'].print_help()
return False
if not cli.args.keymap:
cli.log.error('Missing parameter: --keymap')
cli.subcommands['xap-generate-info-h'].print_help()
return False
generate_blob(cli.args.output, cli.args.keyboard, cli.args.keymap)

View file

@ -0,0 +1,217 @@
"""Interactions with compatible XAP devices
"""
import cmd
from milc import cli
from qmk.keyboard import render_layout
from qmk.xap.common import get_xap_keycodes
from .xap_client import XAPClient, XAPEventType, XAPSecureStatus
KEYCODE_MAP = get_xap_keycodes('latest')
def print_dotted_output(kb_info_json, prefix=''):
"""Print the info.json in a plain text format with dot-joined keys.
"""
for key in sorted(kb_info_json):
new_prefix = f'{prefix}.{key}' if prefix else key
if key in ['parse_errors', 'parse_warnings']:
continue
elif key == 'layouts' and prefix == '':
cli.echo(' {fg_blue}layouts{fg_reset}: %s', ', '.join(sorted(kb_info_json['layouts'].keys())))
elif isinstance(kb_info_json[key], bytes):
conv = "".join(["{:02X}".format(b) for b in kb_info_json[key]])
cli.echo(' {fg_blue}%s{fg_reset}: %s', new_prefix, conv)
elif isinstance(kb_info_json[key], dict):
print_dotted_output(kb_info_json[key], new_prefix)
elif isinstance(kb_info_json[key], list):
data = kb_info_json[key]
if len(data) and isinstance(data[0], dict):
for index, item in enumerate(data, start=0):
cli.echo(' {fg_blue}%s.%s{fg_reset}: %s', new_prefix, index, str(item))
else:
cli.echo(' {fg_blue}%s{fg_reset}: %s', new_prefix, ', '.join(map(str, data)))
else:
cli.echo(' {fg_blue}%s{fg_reset}: %s', new_prefix, kb_info_json[key])
def _list_devices():
"""Dump out available devices
"""
cli.log.info('Available devices:')
devices = XAPClient.list()
for dev in devices:
device = XAPClient().connect(dev)
data = device.info()
cli.log.info(' %04x:%04x %s %s [API:%s]', dev['vendor_id'], dev['product_id'], dev['manufacturer_string'], dev['product_string'], data['xap'])
if cli.config.general.verbose:
# TODO: better formatting like 'lsusb -v'?
print_dotted_output(data)
class XAPShell(cmd.Cmd):
intro = 'Welcome to the XAP shell. Type help or ? to list commands.\n'
prompt = 'Ψ> '
def __init__(self, device):
cmd.Cmd.__init__(self)
self.device = device
# cache keycodes for this device
self.keycodes = get_xap_keycodes(device.version()['xap'])
def do_about(self, arg):
"""Prints out the current version of QMK with a build date
"""
# TODO: request stuff?
print(self.device.info()['xap'])
def do_status(self, arg):
"""Prints out the current device state
"""
status = self.device.status()
print('Secure:%s' % status.get('lock', '???'))
def do_unlock(self, arg):
"""Initiate secure unlock
"""
self.device.unlock()
print('Unlock Requested...')
def do_lock(self, arg):
"""Disable secure routes
"""
self.device.lock()
def do_reset(self, arg):
"""Jump to bootloader if unlocked
"""
if not self.device.reset():
print("Reboot to bootloader failed")
return True
def do_listen(self, arg):
"""Log out XAP broadcast messages
"""
try:
cli.log.info('Listening for XAP broadcasts...')
while 1:
(event, data) = self.device.listen()
if event == XAPEventType.SECURE:
secure_status = XAPSecureStatus(data[0]).name
cli.log.info(' Secure[%s]', secure_status)
else:
cli.log.info(' Broadcast: type[%02x] data:[%s]', event, data.hex())
except KeyboardInterrupt:
cli.log.info('Stopping...')
def do_keycode(self, arg):
"""Prints out the keycode value of a certain layer, row, and column
"""
data = bytes(map(int, arg.split()))
if len(data) != 3:
cli.log.error('Invalid args')
return
keycode = self.device.transaction(b'\x04\x03', data)
keycode = int.from_bytes(keycode, 'little')
print(f'keycode:{self.keycodes.get(keycode, "unknown")}[{keycode}]')
def do_keymap(self, arg):
"""Prints out the keycode values of a certain layer
"""
data = bytes(map(int, arg.split()))
if len(data) != 1:
cli.log.error('Invalid args')
return
info = self.device.info()
rows = info['matrix_size']['rows']
cols = info['matrix_size']['cols']
for r in range(rows):
for c in range(cols):
q = data + r.to_bytes(1, byteorder='little') + c.to_bytes(1, byteorder='little')
keycode = self.device.transaction(b'\x04\x03', q)
keycode = int.from_bytes(keycode, 'little')
print(f'| {self.keycodes.get(keycode, "unknown").ljust(7)} ', end='', flush=True)
print('|')
def do_layer(self, arg):
"""Renders keycode values of a certain layer
"""
data = bytes(map(int, arg.split()))
if len(data) != 1:
cli.log.error('Invalid args')
return
info = self.device.info()
# Assumptions on selected layout rather than prompt
first_layout = next(iter(info['layouts']))
layout = info['layouts'][first_layout]['layout']
keycodes = []
for item in layout:
q = data + bytes(item['matrix'])
keycode = self.device.transaction(b'\x04\x03', q)
keycode = int.from_bytes(keycode, 'little')
keycodes.append(self.keycodes.get(keycode, '???'))
print(render_layout(layout, False, keycodes))
def do_exit(self, line):
"""Quit shell
"""
return True
def do_EOF(self, line): # noqa: N802
"""Quit shell (ctrl+D)
"""
return True
def loop(self):
"""Wrapper for cmdloop that handles ctrl+C
"""
try:
self.cmdloop()
print('')
except KeyboardInterrupt:
print('^C')
return False
@cli.argument('-d', '--device', help='device to select - uses format <pid>:<vid>.')
@cli.argument('-l', '--list', arg_only=True, action='store_true', help='List available devices.')
@cli.argument('-i', '--interactive', arg_only=True, action='store_true', help='Start interactive shell.')
@cli.argument('action', nargs='*', default=['listen'], arg_only=True)
@cli.subcommand('Acquire debugging information from usb XAP devices.', hidden=False if cli.config.user.developer else True)
def xap(cli):
"""Acquire debugging information from XAP devices
"""
if cli.args.list:
return _list_devices()
# Connect to first available device
devices = XAPClient.list()
if not devices:
cli.log.error('No devices found!')
return False
dev = devices[0]
cli.log.info('Connecting to:%04x:%04x %s %s', dev['vendor_id'], dev['product_id'], dev['manufacturer_string'], dev['product_string'])
device = XAPClient().connect(dev)
# shell?
if cli.args.interactive:
XAPShell(device).loop()
return True
XAPShell(device).onecmd(' '.join(cli.args.action))

View file

@ -0,0 +1,200 @@
"""Dummy XAP Client
"""
import json
import random
import gzip
import threading
import functools
from struct import Struct, pack, unpack
from collections import namedtuple
from enum import IntFlag, IntEnum
from platform import platform
RequestPacket = namedtuple('RequestPacket', 'token length data')
RequestStruct = Struct('<HB61s')
ResponsePacket = namedtuple('ResponsePacket', 'token flags length data')
ResponseStruct = Struct('<HBB60s')
def _gen_token():
"""Generate XAP token - cannot start with 00xx or 'reserved' (FFFE|FFFF)
"""
token = random.randrange(0x0100, 0xFFFD)
# swap endianness
return unpack('<H', pack('>H', token))[0]
def _u32toBCD(val): # noqa: N802
"""Create BCD string
"""
return f'{val>>24}.{val>>16 & 0xFF}.{val & 0xFFFF}'
class XAPSecureStatus(IntEnum):
LOCKED = 0x00
UNLOCKING = 0x01
UNLOCKED = 0x02
class XAPFlags(IntFlag):
FAILURE = 0
SUCCESS = 1 << 0
SECURE_FAILURE = 1 << 1
UNLOCK_IN_PROGRESS = 1 << 6
UNLOCKED = 1 << 7
class XAPEventType(IntEnum):
SECURE = 0x01
KEYBOARD = 0x02
USER = 0x03
class XAPDevice:
def __init__(self, dev):
"""Constructor opens hid device and starts dependent services
"""
self.responses = {}
self.dev = hid.Device(path=dev['path'])
self.bg = threading.Thread(target=self._read_loop, daemon=True)
self.bg.start()
def _read_loop(self):
"""Background thread to signal waiting transactions
"""
while 1:
array_alpha = self.dev.read(64, 100)
if array_alpha:
token = int.from_bytes(array_alpha[:2], 'little')
event = self.responses.get(token)
if event:
event._ret = array_alpha
event.set()
def _query_device_info(self):
datalen = int.from_bytes(self.transaction(b'\x01\x05') or bytes(0), 'little')
if not datalen:
return {}
data = []
offset = 0
while offset < datalen:
chunk = self.transaction(b'\x01\x06', offset)
data += chunk
offset += len(chunk)
str_data = gzip.decompress(bytearray(data[:datalen]))
return json.loads(str_data)
def listen(self):
"""Receive a 'broadcast' message
"""
token = 0xFFFF
event = threading.Event()
self.responses[token] = event
event.wait()
r = ResponsePacket._make(ResponseStruct.unpack(event._ret))
return (r.flags, r.data[:r.length])
def transaction(self, *args):
"""Request/Receive
"""
# convert args to array of bytes
data = bytes()
for arg in args:
if isinstance(arg, (bytes, bytearray)):
data += arg
if isinstance(arg, int): # TODO: remove terrible assumption of u16
data += arg.to_bytes(2, byteorder='little')
token = _gen_token()
p = RequestPacket(token, len(data), data)
buffer = RequestStruct.pack(*list(p))
event = threading.Event()
self.responses[token] = event
# prepend 0 on windows because reasons...
if 'windows' in platform().lower():
buffer = b'\x00' + buffer
self.dev.write(buffer)
event.wait(timeout=1)
self.responses.pop(token, None)
if not hasattr(event, '_ret'):
return None
r = ResponsePacket._make(ResponseStruct.unpack(event._ret))
if r.flags != XAPFlags.SUCCESS:
return None
return r.data[:r.length]
@functools.cache
def version(self):
ver = int.from_bytes(self.transaction(b'\x00\x00') or bytes(0), 'little')
return {'xap': _u32toBCD(ver)}
@functools.cache
def info(self):
data = self._query_device_info()
data['_id'] = self.transaction(b'\x01\x08')
data['xap'] = self.version()['xap']
return data
def status(self):
lock = int.from_bytes(self.transaction(b'\x00\x03') or bytes(0), 'little')
data = {}
data['lock'] = XAPSecureStatus(lock).name
return data
def unlock(self):
self.transaction(b'\x00\x04')
def lock(self):
self.transaction(b'\x00\x05')
def reset(self):
status = int.from_bytes(self.transaction(b'\x01\x07') or bytes(0), 'little')
return status == 1
class XAPClient:
@staticmethod
def _lazy_imports():
# Lazy load to avoid missing dependency issues
global hid
import hid
@staticmethod
def list(search=None):
"""Find compatible XAP devices
"""
XAPClient._lazy_imports()
def _is_xap_usage(x):
return x['usage_page'] == 0xFF51 and x['usage'] == 0x0058
def _is_filtered_device(x):
name = '%04x:%04x' % (x['vendor_id'], x['product_id'])
return name.lower().startswith(search.lower())
devices = filter(_is_xap_usage, hid.enumerate())
if search:
devices = filter(_is_filtered_device, devices)
return list(devices)
def connect(self, dev):
"""Connect to a given XAP device
"""
XAPClient._lazy_imports()
return XAPDevice(dev)

View file

@ -3,6 +3,7 @@
import os
import sys
import shutil
from itertools import islice
from pathlib import Path
from milc import cli
@ -219,6 +220,13 @@ def in_virtualenv():
return active_prefix != sys.prefix
def get_chunks(it, size):
"""Break down a collection into smaller parts
"""
it = iter(it)
return iter(lambda: tuple(islice(it, size)), ())
def dump_lines(output_file, lines, quiet=True):
"""Handle dumping to stdout or file
Creates parent folders if required

View file

@ -4,6 +4,7 @@ from array import array
from math import ceil
from pathlib import Path
import os
import shutil
from glob import glob
import qmk.path
@ -11,6 +12,7 @@ from qmk.c_parse import parse_config_h_file
from qmk.json_schema import json_load
from qmk.makefile import parse_rules_mk_file
KEY_WIDTH = 4 if shutil.get_terminal_size().columns < 160 else 6
BOX_DRAWING_CHARACTERS = {
"unicode": {
"tl": "",
@ -205,9 +207,9 @@ def render_layouts(info_json, render_ascii):
def render_key_rect(textpad, x, y, w, h, label, style):
box_chars = BOX_DRAWING_CHARACTERS[style]
x = ceil(x * 4)
x = ceil(x * KEY_WIDTH)
y = ceil(y * 3)
w = ceil(w * 4)
w = ceil(w * KEY_WIDTH)
h = ceil(h * 3)
label_len = w - 2
@ -234,9 +236,9 @@ def render_key_rect(textpad, x, y, w, h, label, style):
def render_key_isoenter(textpad, x, y, w, h, label, style):
box_chars = BOX_DRAWING_CHARACTERS[style]
x = ceil(x * 4)
x = ceil(x * KEY_WIDTH)
y = ceil(y * 3)
w = ceil(w * 4)
w = ceil(w * KEY_WIDTH)
h = ceil(h * 3)
label_len = w - 1
@ -266,9 +268,9 @@ def render_key_isoenter(textpad, x, y, w, h, label, style):
def render_key_baenter(textpad, x, y, w, h, label, style):
box_chars = BOX_DRAWING_CHARACTERS[style]
x = ceil(x * 4)
x = ceil(x * KEY_WIDTH)
y = ceil(y * 3)
w = ceil(w * 4)
w = ceil(w * KEY_WIDTH)
h = ceil(h * 3)
label_len = w - 2

View file

176
lib/python/qmk/xap/common.py Executable file
View file

@ -0,0 +1,176 @@
"""This script handles the XAP protocol data files.
"""
import os
import hjson
import jsonschema
from pathlib import Path
from typing import OrderedDict
from jinja2 import Environment, FileSystemLoader, select_autoescape
from qmk.constants import QMK_FIRMWARE
from qmk.json_schema import json_load, validate
from qmk.decorators import lru_cache
from qmk.keymap import locate_keymap
from qmk.path import keyboard
XAP_SPEC = 'xap.hjson'
def _get_jinja2_env(data_templates_xap_subdir: str):
templates_dir = os.path.join(QMK_FIRMWARE, 'data', 'templates', 'xap', data_templates_xap_subdir)
j2 = Environment(loader=FileSystemLoader(templates_dir), autoescape=select_autoescape())
return j2
def render_xap_output(data_templates_xap_subdir, file_to_render, defs):
j2 = _get_jinja2_env(data_templates_xap_subdir)
return j2.get_template(file_to_render).render(xap=defs, xap_str=hjson.dumps(defs))
def _find_kb_spec(kb):
base_path = Path('keyboards')
keyboard_parent = keyboard(kb)
for _ in range(5):
if keyboard_parent == base_path:
break
spec = keyboard_parent / XAP_SPEC
if spec.exists():
return spec
keyboard_parent = keyboard_parent.parent
# Just return something we know doesn't exist
return keyboard(kb) / XAP_SPEC
def _find_km_spec(kb, km):
return locate_keymap(kb, km).parent / XAP_SPEC
def _merge_ordered_dicts(dicts):
"""Merges nested OrderedDict objects resulting from reading a hjson file.
Later input dicts overrides earlier dicts for plain values.
Arrays will be appended. If the first entry of an array is "!reset!", the contents of the array will be cleared and replaced with RHS.
Dictionaries will be recursively merged. If any entry is "!reset!", the contents of the dictionary will be cleared and replaced with RHS.
"""
result = OrderedDict()
def add_entry(target, k, v):
if k in target and isinstance(v, (OrderedDict, dict)):
if "!reset!" in v:
target[k] = v
else:
target[k] = _merge_ordered_dicts([target[k], v])
if "!reset!" in target[k]:
del target[k]["!reset!"]
elif k in target and isinstance(v, list):
if v[0] == '!reset!':
target[k] = v[1:]
else:
target[k] = target[k] + v
else:
target[k] = v
for d in dicts:
for (k, v) in d.items():
add_entry(result, k, v)
return result
def get_xap_definition_files():
"""Get the sorted list of XAP definition files, from <QMK>/data/xap.
"""
xap_defs = QMK_FIRMWARE / "data" / "xap"
return list(sorted(xap_defs.glob('**/xap_*.hjson')))
def update_xap_definitions(original, new):
"""Creates a new XAP definition object based on an original and the new supplied object.
Both inputs must be of type OrderedDict.
Later input dicts overrides earlier dicts for plain values.
Arrays will be appended. If the first entry of an array is "!reset!", the contents of the array will be cleared and replaced with RHS.
Dictionaries will be recursively merged. If any entry is "!reset!", the contents of the dictionary will be cleared and replaced with RHS.
"""
if original is None:
original = OrderedDict()
return _merge_ordered_dicts([original, new])
@lru_cache(timeout=5)
def get_xap_defs(version):
"""Gets the required version of the XAP definitions.
"""
files = get_xap_definition_files()
# Slice off anything newer than specified version
if version != 'latest':
index = [idx for idx, s in enumerate(files) if version in str(s)][0]
files = files[:(index + 1)]
definitions = [hjson.load(file.open(encoding='utf-8')) for file in files]
return _merge_ordered_dicts(definitions)
def latest_xap_defs():
"""Gets the latest version of the XAP definitions.
"""
return get_xap_defs('latest')
def merge_xap_defs(kb, km):
"""Gets the latest version of the XAP definitions and merges in optional keyboard/keymap specs
"""
definitions = [get_xap_defs('latest')]
kb_xap = _find_kb_spec(kb)
if kb_xap.exists():
definitions.append({'routes': {'0x02': hjson.load(kb_xap.open(encoding='utf-8'))}})
km_xap = _find_km_spec(kb, km)
if km_xap.exists():
definitions.append({'routes': {'0x03': hjson.load(km_xap.open(encoding='utf-8'))}})
defs = _merge_ordered_dicts(definitions)
try:
validate(defs, 'qmk.xap.v1')
except jsonschema.ValidationError as e:
print(f'Invalid XAP spec: {e.message}')
exit(1)
return defs
@lru_cache(timeout=5)
def get_xap_keycodes(xap_version):
"""Gets keycode data for the required version of the XAP definitions.
"""
defs = get_xap_defs(xap_version)
# Load DD keycodes for the dependency
keycode_version = defs['uses']['keycodes']
spec = json_load(Path(f'data/constants/keycodes_{keycode_version}.json'))
# Transform into something more usable - { raw_value : first alias || keycode }
return {int(k, 16): v.get('aliases', [v.get('key')])[0] for k, v in spec['keycodes'].items()}
def route_conditions(route_stack):
"""Handles building the C preprocessor conditional based on the current route.
"""
conditions = []
for route in route_stack:
if 'enable_if_preprocessor' in route:
conditions.append(route['enable_if_preprocessor'])
if len(conditions) == 0:
return None
return "(" + ' && '.join([f'({c})' for c in conditions]) + ")"

View file

@ -0,0 +1,57 @@
"""This script generates the XAP info.json payload header to be compiled into QMK.
"""
import json
import gzip
from pathlib import Path
from qmk.info import keymap_json
from qmk.commands import get_chunks, dump_lines
from qmk.json_schema import deep_update, json_load
from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE
def _build_info(keyboard, keymap):
"""Build the xap version of info.json
"""
defaults_json = json_load(Path('data/mappings/xap_defaults.json'))
km_info_json = keymap_json(keyboard, keymap)
info_json = {}
deep_update(info_json, defaults_json)
deep_update(info_json, km_info_json)
# TODO: Munge to XAP requirements
del info_json['config_h_features']
return info_json
def generate_blob(output_file, keyboard, keymap):
"""Generate XAP payload
"""
info_json = _build_info(keyboard, keymap)
# Minify
str_data = json.dumps(info_json, separators=(',', ':'))
# Compress
compressed = gzip.compress(str_data.encode("utf-8"), compresslevel=9)
# split into lines to match xxd output
hex_array = ["0x{:02X}".format(b) for b in compressed]
data_len = len(hex_array)
data = ""
for chunk in get_chunks(hex_array, 12):
data += f' {", ".join(chunk)},\n'
lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', '']
# Gen output file
lines.append('static const unsigned char config_blob_gz[] PROGMEM = {')
lines.append(data)
lines.append('};')
lines.append(f'#define CONFIG_BLOB_GZ_LEN {data_len}')
dump_lines(output_file, lines)

View file

@ -0,0 +1,268 @@
"""This script generates the XAP protocol generated header to be compiled into QMK.
"""
import re
from fnvhash import fnv1a_32
from qmk.casing import to_snake
from qmk.commands import dump_lines
from qmk.git import git_get_version
from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE
from qmk.xap.common import merge_xap_defs, route_conditions
def _get_c_type(xap_type):
if xap_type == 'bool':
return 'bool'
elif xap_type == 'u8':
return 'uint8_t'
elif xap_type == 'u16':
return 'uint16_t'
elif xap_type == 'u32':
return 'uint32_t'
elif xap_type == 'u64':
return 'uint64_t'
elif xap_type == 'struct':
return 'struct'
elif xap_type == 'string':
return 'const char *'
return 'unknown'
def _append_route_defines(lines, container, container_id=None, route_stack=None):
"""Handles building the list of the XAP routes, combining parent and child names together, as well as the route number.
"""
if route_stack is None:
route_stack = [container]
else:
route_stack.append(container)
route_name = '_'.join([r['define'] for r in route_stack])
if container_id:
lines.append(f'#define {route_name} {container_id}')
if 'routes' in container:
for route_id in container['routes']:
route = container['routes'][route_id]
_append_route_defines(lines, route, route_id, route_stack)
route_stack.pop()
def _append_route_masks(lines, container, container_id=None, route_stack=None):
"""Handles creating the equivalent XAP route masks, for capabilities checks. Forces value of `0` if disabled in the firmware.
"""
if route_stack is None:
route_stack = [container]
else:
route_stack.append(container)
route_name = '_'.join([r['define'] for r in route_stack])
condition = route_conditions(route_stack)
if container_id:
if condition:
lines.append('')
lines.append(f'#if {condition}')
lines.append(f'#define {route_name}_MASK (1ul << ({route_name}))')
if condition:
lines.append(f'#else // {condition}')
lines.append(f'#define {route_name}_MASK 0')
lines.append(f'#endif // {condition}')
lines.append('')
if 'routes' in container:
for route_id in container['routes']:
route = container['routes'][route_id]
_append_route_masks(lines, route, route_id, route_stack)
route_stack.pop()
def _append_route_capabilities(lines, container, container_id=None, route_stack=None):
"""Handles creating the equivalent XAP route masks, for capabilities checks. Forces value of `0` if disabled in the firmware.
"""
if route_stack is None:
route_stack = [container]
else:
route_stack.append(container)
route_name = '_'.join([r['define'] for r in route_stack])
if 'routes' in container:
lines.append('')
lines.append(f'#define {route_name}_CAPABILITIES (0 \\')
if 'routes' in container:
for route_id in container['routes']:
route = container['routes'][route_id]
route_stack.append(route)
child_name = '_'.join([r['define'] for r in route_stack])
lines.append(f' | ({child_name}_MASK) \\')
route_stack.pop()
lines.append(' )')
if 'routes' in container:
for route_id in container['routes']:
route = container['routes'][route_id]
_append_route_capabilities(lines, route, route_id, route_stack)
route_stack.pop()
def _append_route_types(lines, container, container_id=None, route_stack=None):
"""Handles creating typedefs used by routes
"""
if route_stack is None:
route_stack = [container]
else:
route_stack.append(container)
route_name = to_snake('_'.join([r['define'] for r in route_stack]))
# Inbound
if 'request_struct_members' in container:
request_struct_members = container['request_struct_members']
lines.append('typedef struct {')
for member in request_struct_members:
member_type = _get_c_type(member['type'])
member_name = to_snake(member['name'])
lines.append(f' {member_type} {member_name};')
lines.append(f'}} __attribute__((__packed__)) {route_name}_arg_t;')
req_len = container['request_struct_length']
lines.append(f'_Static_assert(sizeof({route_name}_arg_t) == {req_len}, "{route_name}_arg_t needs to be {req_len} bytes in size");')
elif 'request_type' in container:
request_type = container['request_type']
found = re.search(r'(u\d+)\[(\d+)\]', request_type)
if found:
request_type, size = found.groups()
lines.append(f'typedef struct __attribute__((__packed__)) {{ {_get_c_type(request_type)} x[{size}]; }} {route_name}_arg_t;')
else:
lines.append(f'typedef {_get_c_type(request_type)} {route_name}_arg_t;')
# Outbound
qualifier = 'const' if 'return_constant' in container else ''
if 'return_struct_members' in container:
return_struct_members = container['return_struct_members']
lines.append('typedef struct {')
for member in return_struct_members:
member_type = _get_c_type(member['type'])
member_name = f'{qualifier} {to_snake(member["name"])}'
lines.append(f' {member_type} {member_name};')
lines.append(f'}} __attribute__((__packed__)) {route_name}_t;')
req_len = container['return_struct_length']
lines.append(f'_Static_assert(sizeof({route_name}_t) == {req_len}, "{route_name}_t needs to be {req_len} bytes in size");')
elif 'return_type' in container:
return_type = container['return_type']
found = re.search(r'(u\d+)\[(\d+)\]', return_type)
if found:
return_type, size = found.groups()
lines.append(f'typedef struct __attribute__((__packed__)) {{ {_get_c_type(return_type)} x[{size}]; }} {route_name}_t;')
else:
lines.append(f'typedef {_get_c_type(return_type)} {route_name}_t;')
# Recurse
if 'routes' in container:
for route_id in container['routes']:
route = container['routes'][route_id]
_append_route_types(lines, route, route_id, route_stack)
route_stack.pop()
def _append_internal_types(lines, container):
"""Handles creating the various constants, types, defines, etc.
"""
response_flags = container.get('response_flags', {})
prefix = response_flags['define_prefix']
for key, value in response_flags['bits'].items():
define = value.get('define')
lines.append(f'#define {prefix}_{define} (1ul << ({key}))')
# Add special
lines.append(f'#define {prefix}_FAILED 0x00')
lines.append('')
broadcast_messages = container.get('broadcast_messages', {})
broadcast_prefix = broadcast_messages['define_prefix']
for key, value in broadcast_messages['messages'].items():
define = value.get('define')
name = to_snake(f'{broadcast_prefix}_{define}')
lines.append(f'#define {broadcast_prefix}_{define} {key}')
if 'return_type' in value:
ret_type = _get_c_type(value['return_type'])
lines.append(f'void {name}({ret_type} value);')
else:
lines.append(f'void {name}(const void *data, size_t length);')
# Add special
lines.append(f'#define {broadcast_prefix}_TOKEN 0xFFFF')
lines.append('')
additional_types = {}
types = container.get('type_definitions', {})
for key, value in types.items():
data_type = _get_c_type(value['type'])
additional_types[key] = f'xap_{key}_t'
for key, value in types.items():
data_type = _get_c_type(value['type'])
if data_type == 'struct':
members = value['struct_members']
lines.append(f'typedef {data_type} {{')
for member in members:
member_name = member["name"]
member_type = _get_c_type(member["type"])
if member_type == 'unknown':
member_type = additional_types[member["type"]]
lines.append(f' {member_type} {member_name};')
lines.append(f'}} __attribute__((__packed__)) xap_{key}_t;')
req_len = value['struct_length']
lines.append(f'_Static_assert(sizeof(xap_{key}_t) == {req_len}, "xap_{key}_t needs to be {req_len} bytes in size");')
else:
lines.append(f'typedef {data_type} xap_{key}_t;')
def generate_header(output_file, keyboard, keymap):
"""Generates the XAP protocol header file, generated during normal build.
"""
xap_defs = merge_xap_defs(keyboard, keymap)
# Preamble
lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', '']
# Versions
prog = re.compile(r'^(\d+)\.(\d+)\.(\d+)')
b = prog.match(xap_defs['version'])
lines.append(f'#define XAP_BCD_VERSION 0x{int(b.group(1)):02d}{int(b.group(2)):02d}{int(b.group(3)):04d}ul')
b = prog.findall(git_get_version() or "") or [('0', '0', '0')]
lines.append(f'#define QMK_BCD_VERSION 0x{int(b[0][0]):02d}{int(b[0][1]):02d}{int(b[0][1]):04d}ul')
keyboard_id = fnv1a_32(bytes(keyboard, 'utf-8'))
lines.append(f'#define XAP_KEYBOARD_IDENTIFIER 0x{keyboard_id:08X}ul')
lines.append('')
# Types
_append_internal_types(lines, xap_defs)
lines.append('')
_append_route_types(lines, xap_defs)
lines.append('')
# Append the route and command defines
_append_route_defines(lines, xap_defs)
lines.append('')
_append_route_masks(lines, xap_defs)
lines.append('')
_append_route_capabilities(lines, xap_defs)
lines.append('')
dump_lines(output_file, lines)

View file

@ -0,0 +1,283 @@
"""This script generates the XAP protocol generated header to be compiled into QMK.
"""
from qmk.casing import to_snake
from qmk.commands import dump_lines
from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE
from qmk.xap.common import merge_xap_defs, route_conditions
def _get_c_type(xap_type):
if xap_type == 'bool':
return 'bool'
elif xap_type == 'u8':
return 'uint8_t'
elif xap_type == 'u16':
return 'uint16_t'
elif xap_type == 'u32':
return 'uint32_t'
elif xap_type == 'u64':
return 'uint64_t'
elif xap_type == 'struct':
return 'struct'
elif xap_type == 'string':
return 'const char *'
return 'unknown'
def _get_c_size(xap_type):
if xap_type == 'u8':
return 'sizeof(uint8_t)'
elif xap_type == 'u16':
return 'sizeof(uint16_t)'
elif xap_type == 'u32':
return 'sizeof(uint32_t)'
elif xap_type == 'u64':
return 8
elif xap_type == 'u8[32]':
return 32
return 0
def _get_route_type(container):
if 'routes' in container:
return 'XAP_ROUTE'
elif 'return_execute' in container:
return 'XAP_EXECUTE'
elif 'return_value' in container:
if container['return_type'] == 'u8':
return 'XAP_VALUE'
elif 'return_constant' in container:
if container['return_type'] == 'u8':
return 'XAP_CONST_MEM'
elif container['return_type'] == 'u16':
return 'XAP_CONST_MEM'
elif container['return_type'] == 'u32':
return 'XAP_CONST_MEM'
elif container['return_type'] == 'struct':
return 'XAP_CONST_MEM'
elif container['return_type'] == 'string':
return 'XAP_CONST_MEM'
elif 'return_getter' in container:
if container['return_type'] == 'u32':
return 'XAP_GETTER'
return 'UNSUPPORTED'
def _append_routing_table_declaration(lines, container, container_id, route_stack):
route_stack.append(container)
route_name = to_snake('_'.join([r['define'] for r in route_stack]))
if 'routes' in container:
pass
elif 'return_execute' in container:
execute = container['return_execute']
lines.append(f'bool xap_respond_{execute}(xap_token_t token, const uint8_t *data, size_t data_len);')
# elif 'return_value' in container:
# value = container['return_value']
# return_type = container['return_type']
# lines.append('')
# lines.append(f'{_get_c_type(return_type)} {value} = 0;')
elif 'return_constant' in container:
if container['return_type'] == 'u8':
constant = container['return_constant']
lines.append('')
lines.append(f'static const uint8_t {route_name}_data PROGMEM = {constant};')
elif container['return_type'] == 'u16':
constant = container['return_constant']
lines.append('')
lines.append(f'static const uint16_t {route_name}_data PROGMEM = {constant};')
elif container['return_type'] == 'u32':
constant = container['return_constant']
lines.append('')
lines.append(f'static const uint32_t {route_name}_data PROGMEM = {constant};')
elif container['return_type'] == 'struct':
lines.append('')
lines.append(f'static const {route_name}_t {route_name}_data PROGMEM = {{')
for constant in container['return_constant']:
lines.append(f' {constant},')
lines.append('};')
elif container['return_type'] == 'string':
constant = container['return_constant']
lines.append('')
lines.append(f'static const char {route_name}_str[] PROGMEM = {constant};')
elif 'return_getter' in container:
if container['return_type'] == 'u32':
lines.append('')
lines.append(f'extern uint32_t {route_name}_getter(void);')
elif container['return_type'] == 'struct':
pass
route_stack.pop()
def _append_routing_table_entry_flags(lines, container, container_id, route_stack):
pem_map = {
None: 'ROUTE_PERMISSIONS_INSECURE',
'secure': 'ROUTE_PERMISSIONS_SECURE',
'ignore': 'ROUTE_PERMISSIONS_IGNORE',
}
is_secure = pem_map[container.get('permissions', None)]
lines.append(' .flags = {')
lines.append(f' .type = {_get_route_type(container)},')
lines.append(f' .secure = {is_secure},')
lines.append(' },')
def _append_routing_table_entry_route(lines, container, container_id, route_stack):
route_name = to_snake('_'.join([r['define'] for r in route_stack]))
lines.append(f' .child_routes = {route_name}_table,')
lines.append(f' .child_routes_len = sizeof({route_name}_table)/sizeof(xap_route_t),')
def _append_routing_table_entry_execute(lines, container, container_id, route_stack):
value = container['return_execute']
lines.append(f' .handler = xap_respond_{value},')
def _append_routing_table_entry_value(lines, container, container_id, route_stack):
value = container['return_value']
lines.append(f' .const_data = &{value},')
lines.append(f' .const_data_len = sizeof({value}),')
def _append_routing_table_entry_u32getter(lines, container, container_id, route_stack):
route_name = to_snake('_'.join([r['define'] for r in route_stack]))
lines.append(f' .u32getter = &{route_name}_getter,')
def _append_routing_table_entry_const_data(lines, container, container_id, route_stack):
route_name = to_snake('_'.join([r['define'] for r in route_stack]))
lines.append(f' .const_data = &{route_name}_data,')
lines.append(f' .const_data_len = sizeof({route_name}_data),')
def _append_routing_table_entry_string(lines, container, container_id, route_stack):
route_name = to_snake('_'.join([r['define'] for r in route_stack]))
lines.append(f' .const_data = {route_name}_str,')
lines.append(f' .const_data_len = sizeof({route_name}_str) - 1,')
def _append_routing_table_entry(lines, container, container_id, route_stack):
route_stack.append(container)
route_name = '_'.join([r['define'] for r in route_stack])
condition = route_conditions(route_stack)
if condition:
lines.append(f'#if {condition}')
lines.append(f' [{route_name}] = {{')
_append_routing_table_entry_flags(lines, container, container_id, route_stack)
if 'routes' in container:
_append_routing_table_entry_route(lines, container, container_id, route_stack)
elif 'return_execute' in container:
_append_routing_table_entry_execute(lines, container, container_id, route_stack)
elif 'return_value' in container:
_append_routing_table_entry_value(lines, container, container_id, route_stack)
elif 'return_constant' in container:
if container['return_type'] == 'u8':
_append_routing_table_entry_const_data(lines, container, container_id, route_stack)
elif container['return_type'] == 'u16':
_append_routing_table_entry_const_data(lines, container, container_id, route_stack)
elif container['return_type'] == 'u32':
_append_routing_table_entry_const_data(lines, container, container_id, route_stack)
elif container['return_type'] == 'struct':
_append_routing_table_entry_const_data(lines, container, container_id, route_stack)
elif container['return_type'] == 'string':
_append_routing_table_entry_string(lines, container, container_id, route_stack)
elif 'return_getter' in container:
if container['return_type'] == 'u32':
_append_routing_table_entry_u32getter(lines, container, container_id, route_stack)
lines.append(' },')
if condition:
lines.append(f'#endif // {condition}')
route_stack.pop()
def _append_routing_tables(lines, container, container_id=None, route_stack=None):
"""Handles building the list of the XAP routes, combining parent and child names together, as well as the route number.
"""
if route_stack is None:
route_stack = [container]
else:
route_stack.append(container)
route_name = to_snake('_'.join([r['define'] for r in route_stack]))
condition = route_conditions(route_stack)
if 'routes' in container:
for route_id in container['routes']:
route = container['routes'][route_id]
_append_routing_tables(lines, route, route_id, route_stack)
for route_id in container['routes']:
route = container['routes'][route_id]
_append_routing_table_declaration(lines, route, route_id, route_stack)
lines.append('')
if condition:
lines.append(f'#if {condition}')
lines.append(f'static const xap_route_t {route_name}_table[] PROGMEM = {{')
for route_id in container['routes']:
route = container['routes'][route_id]
_append_routing_table_entry(lines, route, route_id, route_stack)
lines.append('};')
if condition:
lines.append(f'#endif // {condition}')
lines.append('')
route_stack.pop()
def _append_broadcast_messages(lines, container):
"""TODO:
"""
broadcast_messages = container.get('broadcast_messages', {})
broadcast_prefix = broadcast_messages['define_prefix']
for key, value in broadcast_messages['messages'].items():
define = value.get('define')
name = to_snake(f'{broadcast_prefix}_{define}')
if 'return_type' in value:
ret_type = _get_c_type(value['return_type'])
lines.append(f'void {name}({ret_type} value) {{ xap_broadcast({key}, &value, sizeof(value)); }}')
else:
lines.append(f'void {name}(const void *data, size_t length){{ xap_broadcast({key}, data, length); }}')
def generate_inline(output_file, keyboard, keymap):
"""Generates the XAP protocol header file, generated during normal build.
"""
xap_defs = merge_xap_defs(keyboard, keymap)
# Preamble
lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '']
# Add all the generated code
_append_broadcast_messages(lines, xap_defs)
_append_routing_tables(lines, xap_defs)
dump_lines(output_file, lines)

View file

@ -16,6 +16,9 @@
bool via_eeprom_is_valid(void);
void via_eeprom_set_valid(bool valid);
void eeconfig_init_via(void);
#elif defined(DYNAMIC_KEYMAP_ENABLE)
# include "keymap_hash.h"
void dynamic_keymap_reset(void);
#endif
/** \brief eeconfig enable
@ -81,6 +84,9 @@ void eeconfig_init_quantum(void) {
// properly re-initialized.
via_eeprom_set_valid(false);
eeconfig_init_via();
#elif defined(DYNAMIC_KEYMAP_ENABLE)
dynamic_keymap_reset();
eeprom_update_dword(EECONFIG_KEYMAP_HASH, KEYMAP_HASH);
#endif
eeconfig_init_kb();
@ -119,10 +125,14 @@ void eeconfig_disable(void) {
*/
bool eeconfig_is_enabled(void) {
bool is_eeprom_enabled = (eeprom_read_word(EECONFIG_MAGIC) == EECONFIG_MAGIC_NUMBER);
#ifdef VIA_ENABLE
#if defined(VIA_ENABLE)
if (is_eeprom_enabled) {
is_eeprom_enabled = via_eeprom_is_valid();
}
#elif defined(DYNAMIC_KEYMAP_ENABLE)
if (is_eeprom_enabled) {
is_eeprom_enabled = (eeprom_read_dword(EECONFIG_KEYMAP_HASH) == KEYMAP_HASH);
}
#endif
return is_eeprom_enabled;
}
@ -133,10 +143,14 @@ bool eeconfig_is_enabled(void) {
*/
bool eeconfig_is_disabled(void) {
bool is_eeprom_disabled = (eeprom_read_word(EECONFIG_MAGIC) == EECONFIG_MAGIC_NUMBER_OFF);
#ifdef VIA_ENABLE
#if defined(VIA_ENABLE)
if (!is_eeprom_disabled) {
is_eeprom_disabled = !via_eeprom_is_valid();
}
#elif defined(DYNAMIC_KEYMAP_ENABLE)
if (!is_eeprom_disabled) {
is_eeprom_disabled = (eeprom_read_dword(EECONFIG_KEYMAP_HASH) != KEYMAP_HASH);
}
#endif
return is_eeprom_disabled;
}

View file

@ -21,7 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <stdbool.h>
#ifndef EECONFIG_MAGIC_NUMBER
# define EECONFIG_MAGIC_NUMBER (uint16_t)0xFEE8 // When changing, decrement this value to avoid future re-init issues
# define EECONFIG_MAGIC_NUMBER (uint16_t)0xFEE7 // When changing, decrement this value to avoid future re-init issues
#endif
#define EECONFIG_MAGIC_NUMBER_OFF (uint16_t)0xFFFF
@ -53,8 +53,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// TODO: Combine these into a single word and single block of EEPROM
#define EECONFIG_KEYMAP_UPPER_BYTE (uint8_t *)34
#define EECONFIG_KEYMAP_HASH (uint32_t *)35
// Size of EEPROM being used, other code can refer to this for available EEPROM
#define EECONFIG_SIZE 35
#define EECONFIG_SIZE 39
/* debug bit */
#define EECONFIG_DEBUG_ENABLE (1 << 0)
#define EECONFIG_DEBUG_MATRIX (1 << 1)

View file

@ -579,5 +579,9 @@ void secure_hook_quantum(secure_status_t secure_status) {
clear_keyboard();
layer_clear();
}
# if defined(XAP_ENABLE)
xap_broadcast_secure_status(secure_status);
# endif
}
#endif

View file

@ -210,6 +210,10 @@ extern layer_state_t layer_state;
# include "joystick.h"
#endif
#ifdef XAP_ENABLE
# include "xap.h"
#endif
#ifdef VIA_ENABLE
# include "via.h"
#endif

160
quantum/xap/xap.c Normal file
View file

@ -0,0 +1,160 @@
/* Copyright 2021 Nick Brassel (@tzarc)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <quantum.h>
#include <xap.h>
#include "secure.h"
#include "config_blob_gz.h"
bool get_config_blob_chunk(uint16_t offset, uint8_t *data, uint8_t data_len) {
if (offset + data_len > CONFIG_BLOB_GZ_LEN) {
data_len = CONFIG_BLOB_GZ_LEN - offset;
}
memcpy_P(data, &config_blob_gz[offset], data_len);
return true;
}
#define QSTR2(z) #z
#define QSTR(z) QSTR2(z)
typedef enum xap_route_type_t {
XAP_UNUSED = 0, // "Unused" needs to be zero -- undefined routes (through preprocessor) will be skipped
XAP_ROUTE,
XAP_EXECUTE,
XAP_VALUE,
XAP_GETTER,
XAP_CONST_MEM,
TOTAL_XAP_ROUTE_TYPES
} xap_route_type_t;
#define XAP_ROUTE_TYPE_BIT_COUNT 3
typedef enum xap_route_secure_t {
ROUTE_PERMISSIONS_INSECURE,
ROUTE_PERMISSIONS_SECURE,
ROUTE_PERMISSIONS_IGNORE,
} xap_route_secure_t;
#define XAP_ROUTE_SECURE_BIT_COUNT 2
typedef struct __attribute__((packed)) xap_route_flags_t {
xap_route_type_t type : XAP_ROUTE_TYPE_BIT_COUNT;
xap_route_secure_t secure : XAP_ROUTE_SECURE_BIT_COUNT;
} xap_route_flags_t;
_Static_assert(TOTAL_XAP_ROUTE_TYPES <= (1 << (XAP_ROUTE_TYPE_BIT_COUNT)), "Number of XAP route types is too large for XAP_ROUTE_TYPE_BITS.");
_Static_assert(sizeof(xap_route_flags_t) == 1, "xap_route_flags_t is not length of 1");
typedef struct xap_route_t xap_route_t;
struct __attribute__((packed)) xap_route_t {
const xap_route_flags_t flags;
union {
// XAP_ROUTE
struct {
const xap_route_t *child_routes;
const uint8_t child_routes_len;
};
// XAP_EXECUTE
bool (*handler)(xap_token_t token, const uint8_t *data, size_t data_len);
// XAP_GETTER
uint32_t (*u32getter)(void);
// XAP_VALUE / XAP_CONST_MEM
struct {
const void * const_data;
const uint8_t const_data_len;
};
};
};
#include <xap_generated.inl>
bool xap_pre_execute_route(xap_token_t token, const xap_route_t *route) {
#ifdef SECURE_ENABLE
if (!secure_is_unlocked() && (route->flags.secure == ROUTE_PERMISSIONS_SECURE)) {
xap_respond_failure(token, XAP_RESPONSE_FLAG_SECURE_FAILURE);
return true;
}
if (secure_is_unlocking() && (route->flags.type != XAP_ROUTE) && (route->flags.secure != ROUTE_PERMISSIONS_IGNORE)) {
xap_respond_failure(token, XAP_RESPONSE_FLAG_UNLOCK_IN_PROGRESS);
return true;
}
// TODO: XAP messages extend unlocked timeout?
secure_activity_event();
#endif
return false;
}
void xap_execute_route(xap_token_t token, const xap_route_t *routes, size_t max_routes, const uint8_t *data, size_t data_len) {
if (data_len == 0) return;
xap_identifier_t id = data[0];
if (id < max_routes) {
xap_route_t route;
memcpy_P(&route, &routes[id], sizeof(xap_route_t));
if (xap_pre_execute_route(token, &route)) {
return;
}
switch (route.flags.type) {
case XAP_ROUTE:
if (route.child_routes != NULL && route.child_routes_len > 0 && data_len > 0) {
xap_execute_route(token, route.child_routes, route.child_routes_len, &data[1], data_len - 1);
return;
}
break;
case XAP_EXECUTE:
if (route.handler != NULL) {
bool ok = (route.handler)(token, data_len == 1 ? NULL : &data[1], data_len - 1);
if (ok) return;
}
break;
case XAP_GETTER:
if (route.u32getter != NULL) {
const uint32_t ret = (route.u32getter)();
xap_respond_data(token, &ret, sizeof(ret));
return;
}
break;
case XAP_VALUE:
xap_respond_data(token, route.const_data, route.const_data_len);
return;
case XAP_CONST_MEM:
xap_respond_data_P(token, route.const_data, route.const_data_len);
return;
default:
break;
}
}
// Nothing got handled, so we respond with failure.
xap_respond_failure(token, XAP_RESPONSE_FLAG_FAILED);
}
void xap_receive(xap_token_t token, const uint8_t *data, size_t length) {
xap_execute_route(token, xap_route_table, sizeof(xap_route_table) / sizeof(xap_route_t), data, length);
}

38
quantum/xap/xap.h Normal file
View file

@ -0,0 +1,38 @@
/* Copyright 2021 Nick Brassel (@tzarc)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdlib.h>
#include <stdint.h>
#include <xap_generated.h>
#ifndef XAP_SUBSYSTEM_VERSION_KB
# define XAP_SUBSYSTEM_VERSION_KB 0
#endif
#ifndef XAP_SUBSYSTEM_VERSION_USER
# define XAP_SUBSYSTEM_VERSION_USER 0
#endif
void xap_respond_failure(xap_token_t token, xap_response_flags_t response_flags);
bool xap_respond_u32(xap_token_t token, uint32_t value);
bool xap_respond_data(xap_token_t token, const void *data, size_t length);
bool xap_respond_data_P(xap_token_t token, const void *data, size_t length);
void xap_send(xap_token_t token, xap_response_flags_t response_flags, const void *data, size_t length);
void xap_broadcast(uint8_t type, const void *data, size_t length);

161
quantum/xap/xap_handlers.c Normal file
View file

@ -0,0 +1,161 @@
/* Copyright 2021 Nick Brassel (@tzarc)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <quantum.h>
#include <xap.h>
#include "hardware_id.h"
#include "secure.h"
#ifndef SECURE_ENABLE
# define secure_get_status() SECURE_UNLOCKED
# define secure_request_unlock()
# define secure_lock()
#endif
void xap_respond_success(xap_token_t token) {
xap_send(token, XAP_RESPONSE_FLAG_SUCCESS, NULL, 0);
}
void xap_respond_failure(xap_token_t token, xap_response_flags_t response_flags) {
xap_send(token, response_flags, NULL, 0);
}
bool xap_respond_data(xap_token_t token, const void *data, size_t length) {
xap_send(token, XAP_RESPONSE_FLAG_SUCCESS, data, length);
return true;
}
bool xap_respond_data_P(xap_token_t token, const void *data, size_t length) {
uint8_t blob[length];
memcpy_P(blob, data, length);
return xap_respond_data(token, blob, length);
}
bool xap_respond_u32(xap_token_t token, uint32_t value) {
xap_send(token, XAP_RESPONSE_FLAG_SUCCESS, &value, sizeof(value));
return true;
}
uint32_t xap_route_qmk_ffffffffffffffff_getter(void) {
return 0x12345678;
}
bool xap_respond_get_config_blob_chunk(xap_token_t token, const void *data, size_t length) {
if (length != sizeof(uint16_t)) {
return false;
}
uint16_t offset = *((uint16_t *)data);
xap_route_qmk_config_blob_chunk_t ret = {0};
bool get_config_blob_chunk(uint16_t offset, uint8_t * data, uint8_t data_len);
get_config_blob_chunk(offset, (uint8_t *)&ret, sizeof(ret));
return xap_respond_data(token, &ret, sizeof(ret));
}
bool xap_respond_secure_status(xap_token_t token, const void *data, size_t length) {
uint8_t ret = secure_get_status();
return xap_respond_data(token, &ret, sizeof(ret));
}
bool xap_respond_secure_unlock(xap_token_t token, const void *data, size_t length) {
secure_request_unlock();
return xap_respond_data(token, NULL, 0);
}
bool xap_respond_secure_lock(xap_token_t token, const void *data, size_t length) {
secure_lock();
return xap_respond_data(token, NULL, 0);
}
#ifdef BOOTLOADER_JUMP_SUPPORTED
bool xap_respond_request_bootloader_jump(xap_token_t token, const void *data, size_t length) {
uint8_t ret = secure_is_unlocked();
// TODO: post to deferred queue so this request can return?
bool res = xap_respond_data(token, &ret, sizeof(ret));
reset_keyboard();
return res;
}
#endif
bool xap_respond_get_hardware_id(xap_token_t token, const void *data, size_t length) {
hardware_id_t ret = get_hardware_id();
return xap_respond_data(token, &ret, sizeof(ret));
}
bool xap_respond_keymap_get_layer_count(xap_token_t token, const void *data, size_t length) {
uint8_t ret = keymap_layer_count();
return xap_respond_data(token, &ret, sizeof(ret));
}
bool xap_respond_get_keymap_keycode(xap_token_t token, const void *data, size_t length) {
if (length != sizeof(xap_route_keymap_get_keymap_keycode_arg_t)) {
return false;
}
xap_route_keymap_get_keymap_keycode_arg_t *arg = (xap_route_keymap_get_keymap_keycode_arg_t *)data;
keypos_t pos = MAKE_KEYPOS(arg->row, arg->column);
uint16_t keycode = keymap_key_to_keycode(arg->layer, pos);
return xap_respond_data(token, &keycode, sizeof(keycode));
}
#if ((defined(ENCODER_MAP_ENABLE)))
bool xap_respond_get_encoder_keycode(xap_token_t token, const void *data, size_t length) {
if (length != sizeof(xap_route_keymap_get_encoder_keycode_arg_t)) {
return false;
}
xap_route_keymap_get_encoder_keycode_arg_t *arg = (xap_route_keymap_get_encoder_keycode_arg_t *)data;
keypos_t pos = MAKE_KEYPOS(arg->clockwise ? KEYLOC_ENCODER_CW : KEYLOC_ENCODER_CCW, arg->encoder);
uint16_t keycode = keymap_key_to_keycode(arg->layer, pos);
return xap_respond_data(token, &keycode, sizeof(keycode));
}
#endif
#if ((defined(DYNAMIC_KEYMAP_ENABLE)))
bool xap_respond_dynamic_keymap_set_keycode(xap_token_t token, const void *data, size_t length) {
if (length != sizeof(xap_route_remapping_set_keymap_keycode_arg_t)) {
return false;
}
xap_route_remapping_set_keymap_keycode_arg_t *arg = (xap_route_remapping_set_keymap_keycode_arg_t *)data;
dynamic_keymap_set_keycode(arg->layer, arg->row, arg->column, arg->keycode);
xap_respond_success(token);
return true;
}
#endif
#if ((defined(DYNAMIC_KEYMAP_ENABLE) && defined(ENCODER_MAP_ENABLE)))
bool xap_respond_dynamic_encoder_set_keycode(xap_token_t token, const void *data, size_t length) {
if (length != sizeof(xap_route_remapping_set_encoder_keycode_arg_t)) {
return false;
}
xap_route_remapping_set_encoder_keycode_arg_t *arg = (xap_route_remapping_set_encoder_keycode_arg_t *)data;
dynamic_keymap_set_encoder(arg->layer, arg->encoder, arg->clockwise, arg->keycode);
xap_respond_success(token);
return true;
}
#endif

View file

@ -2,8 +2,10 @@
appdirs
argcomplete
colorama
fnvhash
hid
hjson
Jinja2
jsonschema>=4
milc>=1.4.2
pygments

View file

@ -74,6 +74,10 @@ void virtser_task(void);
void raw_hid_task(void);
#endif
#ifdef XAP_ENABLE
void xap_task(void);
#endif
#ifdef CONSOLE_ENABLE
void console_task(void);
#endif
@ -218,4 +222,7 @@ void protocol_post_task(void) {
#ifdef RAW_ENABLE
raw_hid_task();
#endif
#ifdef XAP_ENABLE
xap_task();
#endif
}

View file

@ -54,6 +54,10 @@ extern keymap_config_t keymap_config;
# include "joystick.h"
#endif
#ifdef XAP_ENABLE
# include "xap.h"
#endif
/* ---------------------------------------------------------
* Global interface variables and declarations
* ---------------------------------------------------------
@ -315,6 +319,9 @@ typedef struct {
#ifdef RAW_ENABLE
usb_driver_config_t raw_driver;
#endif
#ifdef XAP_ENABLE
usb_driver_config_t xap_driver;
#endif
#ifdef MIDI_ENABLE
usb_driver_config_t midi_driver;
#endif
@ -352,6 +359,14 @@ static usb_driver_configs_t drivers = {
.raw_driver = QMK_USB_DRIVER_CONFIG(RAW, 0, false),
#endif
#ifdef XAP_ENABLE
# define XAP_IN_CAPACITY 4
# define XAP_OUT_CAPACITY 4
# define XAP_IN_MODE USB_EP_MODE_TYPE_INTR
# define XAP_OUT_MODE USB_EP_MODE_TYPE_INTR
.xap_driver = QMK_USB_DRIVER_CONFIG(XAP, 0, false),
#endif
#ifdef MIDI_ENABLE
# define MIDI_STREAM_IN_CAPACITY 4
# define MIDI_STREAM_OUT_CAPACITY 4
@ -1117,6 +1132,69 @@ void raw_hid_task(void) {
#endif
#ifdef XAP_ENABLE
extern void xap_receive(xap_token_t token, const uint8_t *data, size_t length);
void xap_send_base(uint8_t *data, uint8_t length) {
// TODO: implement variable size packet
if (length != XAP_EPSIZE) {
return;
}
chnWrite(&drivers.xap_driver.driver, data, length);
}
void xap_send(xap_token_t token, xap_response_flags_t response_flags, const void *data, size_t length) {
uint8_t rdata[XAP_EPSIZE] = {0};
xap_response_header_t *header = (xap_response_header_t *)&rdata[0];
header->token = token;
if (length > (XAP_EPSIZE - sizeof(xap_response_header_t))) response_flags &= ~(XAP_RESPONSE_FLAG_SUCCESS);
header->flags = response_flags;
if (response_flags & (XAP_RESPONSE_FLAG_SUCCESS)) {
header->length = (uint8_t)length;
if (data != NULL) {
memcpy(&rdata[sizeof(xap_response_header_t)], data, length);
}
}
xap_send_base(rdata, sizeof(rdata));
}
void xap_broadcast(uint8_t type, const void *data, size_t length) {
uint8_t rdata[XAP_EPSIZE] = {0};
xap_broadcast_header_t *header = (xap_broadcast_header_t *)&rdata[0];
header->token = XAP_BROADCAST_TOKEN;
header->type = type;
if (length > (XAP_EPSIZE - sizeof(xap_broadcast_header_t))) return;
header->length = (uint8_t)length;
if (data != NULL) {
memcpy(&rdata[sizeof(xap_broadcast_header_t)], data, length);
}
xap_send_base(rdata, sizeof(rdata));
}
void xap_receive_base(const void *data) {
const uint8_t * u8data = (const uint8_t *)data;
xap_request_header_t *header = (xap_request_header_t *)&u8data[0];
if (header->length <= (XAP_EPSIZE - sizeof(xap_request_header_t))) {
xap_receive(header->token, &u8data[sizeof(xap_request_header_t)], header->length);
}
}
void xap_task(void) {
uint8_t buffer[XAP_EPSIZE];
size_t size = 0;
do {
size_t size = chnReadTimeout(&drivers.xap_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE);
if (size > 0) {
xap_receive_base(buffer);
}
} while (size > 0);
}
#endif // XAP_ENABLE
#ifdef MIDI_ENABLE
void send_midi_packet(MIDI_EventPacket_t *event) {

View file

@ -86,6 +86,10 @@ extern keymap_config_t keymap_config;
# include "raw_hid.h"
#endif
#ifdef XAP_ENABLE
# include "xap.h"
#endif
#ifdef JOYSTICK_ENABLE
# include "joystick.h"
#endif
@ -209,6 +213,105 @@ static void raw_hid_task(void) {
}
#endif
#ifdef XAP_ENABLE
extern void xap_receive(xap_token_t token, const uint8_t *data, size_t length);
void xap_send_base(uint8_t *data, uint8_t length) {
// TODO: implement variable size packet
if (length != XAP_EPSIZE) {
return;
}
if (USB_DeviceState != DEVICE_STATE_Configured) {
return;
}
// TODO: decide if we allow calls to raw_hid_send() in the middle
// of other endpoint usage.
uint8_t ep = Endpoint_GetCurrentEndpoint();
Endpoint_SelectEndpoint(XAP_IN_EPNUM);
// Check to see if the host is ready to accept another packet
if (Endpoint_IsINReady()) {
// Write data
Endpoint_Write_Stream_LE(data, XAP_EPSIZE, NULL);
// Finalize the stream transfer to send the last packet
Endpoint_ClearIN();
}
Endpoint_SelectEndpoint(ep);
}
void xap_send(xap_token_t token, xap_response_flags_t response_flags, const void *data, size_t length) {
uint8_t rdata[XAP_EPSIZE] = {0};
xap_response_header_t *header = (xap_response_header_t *)&rdata[0];
header->token = token;
if (length > (XAP_EPSIZE - sizeof(xap_response_header_t))) response_flags &= ~(XAP_RESPONSE_FLAG_SUCCESS);
header->flags = response_flags;
if (response_flags & (XAP_RESPONSE_FLAG_SUCCESS)) {
header->length = (uint8_t)length;
if (data != NULL) {
memcpy(&rdata[sizeof(xap_response_header_t)], data, length);
}
}
xap_send_base(rdata, sizeof(rdata));
}
void xap_broadcast(uint8_t type, const void *data, size_t length) {
uint8_t rdata[XAP_EPSIZE] = {0};
xap_broadcast_header_t *header = (xap_broadcast_header_t *)&rdata[0];
header->token = XAP_BROADCAST_TOKEN;
header->type = type;
if (length > (XAP_EPSIZE - sizeof(xap_broadcast_header_t))) return;
header->length = (uint8_t)length;
if (data != NULL) {
memcpy(&rdata[sizeof(xap_broadcast_header_t)], data, length);
}
xap_send_base(rdata, sizeof(rdata));
}
void xap_receive_base(const void *data) {
const uint8_t * u8data = (const uint8_t *)data;
xap_request_header_t *header = (xap_request_header_t *)&u8data[0];
if (header->length <= (XAP_EPSIZE - sizeof(xap_request_header_t))) {
xap_receive(header->token, &u8data[sizeof(xap_request_header_t)], header->length);
}
}
static void xap_task(void) {
// Create a temporary buffer to hold the read in data from the host
uint8_t data[XAP_EPSIZE];
bool data_read = false;
// Device must be connected and configured for the task to run
if (USB_DeviceState != DEVICE_STATE_Configured) return;
Endpoint_SelectEndpoint(XAP_OUT_EPNUM);
// Check to see if a packet has been sent from the host
if (Endpoint_IsOUTReceived()) {
// Check to see if the packet contains data
if (Endpoint_IsReadWriteAllowed()) {
/* Read data */
Endpoint_Read_Stream_LE(data, sizeof(data), NULL);
data_read = true;
}
// Finalize the stream transfer to receive the last packet
Endpoint_ClearOUT();
if (data_read) {
xap_receive_base(data);
}
}
}
#endif // XAP_ENABLE
/*******************************************************************************
* Console
******************************************************************************/
@ -471,6 +574,12 @@ void EVENT_USB_Device_ConfigurationChanged(void) {
ConfigSuccess &= Endpoint_ConfigureEndpoint((RAW_OUT_EPNUM | ENDPOINT_DIR_OUT), EP_TYPE_INTERRUPT, RAW_EPSIZE, 1);
#endif
#ifdef XAP_ENABLE
/* Setup XAP endpoints */
ConfigSuccess &= Endpoint_ConfigureEndpoint((XAP_IN_EPNUM | ENDPOINT_DIR_IN), EP_TYPE_INTERRUPT, XAP_EPSIZE, 1);
ConfigSuccess &= Endpoint_ConfigureEndpoint((XAP_OUT_EPNUM | ENDPOINT_DIR_OUT), EP_TYPE_INTERRUPT, XAP_EPSIZE, 1);
#endif // XAP_ENABLE
#ifdef CONSOLE_ENABLE
/* Setup console endpoint */
ConfigSuccess &= Endpoint_ConfigureEndpoint((CONSOLE_IN_EPNUM | ENDPOINT_DIR_IN), EP_TYPE_INTERRUPT, CONSOLE_EPSIZE, 1);
@ -1096,6 +1205,10 @@ void protocol_post_task(void) {
raw_hid_task();
#endif
#ifdef XAP_ENABLE
xap_task();
#endif
#if !defined(INTERRUPT_CONTROL_ENDPOINT)
USB_USBTask();
#endif

View file

@ -335,6 +335,30 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM RawReport[] = {
};
#endif
#ifdef XAP_ENABLE
const USB_Descriptor_HIDReport_Datatype_t PROGMEM XapReport[] = {
HID_RI_USAGE_PAGE(16, 0xFF51), // Vendor Defined ('Q')
HID_RI_USAGE(8, 0x58), // Vendor Defined ('X')
HID_RI_COLLECTION(8, 0x01), // Application
// Data to host
HID_RI_USAGE(8, 0x62), // Vendor Defined
HID_RI_LOGICAL_MINIMUM(8, 0x00),
HID_RI_LOGICAL_MAXIMUM(16, 0x00FF),
HID_RI_REPORT_COUNT(8, XAP_EPSIZE),
HID_RI_REPORT_SIZE(8, 0x08),
HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
// Data from host
HID_RI_USAGE(8, 0x63), // Vendor Defined
HID_RI_LOGICAL_MINIMUM(8, 0x00),
HID_RI_LOGICAL_MAXIMUM(16, 0x00FF),
HID_RI_REPORT_COUNT(8, XAP_EPSIZE),
HID_RI_REPORT_SIZE(8, 0x08),
HID_RI_OUTPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE | HID_IOF_NON_VOLATILE),
HID_RI_END_COLLECTION(0),
};
#endif // XAP_ENABLE
#ifdef CONSOLE_ENABLE
const USB_Descriptor_HIDReport_Datatype_t PROGMEM ConsoleReport[] = {
HID_RI_USAGE_PAGE(16, 0xFF31), // Vendor Defined (PJRC Teensy compatible)
@ -567,6 +591,56 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = {
},
#endif
#ifdef XAP_ENABLE
/*
* QMK XAP
*/
.Xap_Interface = {
.Header = {
.Size = sizeof(USB_Descriptor_Interface_t),
.Type = DTYPE_Interface
},
.InterfaceNumber = XAP_INTERFACE,
.AlternateSetting = 0x00,
.TotalEndpoints = 2,
.Class = HID_CSCP_HIDClass,
.SubClass = HID_CSCP_NonBootSubclass,
.Protocol = HID_CSCP_NonBootProtocol,
.InterfaceStrIndex = NO_DESCRIPTOR
},
.Xap_HID = {
.Header = {
.Size = sizeof(USB_HID_Descriptor_HID_t),
.Type = HID_DTYPE_HID
},
.HIDSpec = VERSION_BCD(1, 1, 1),
.CountryCode = 0x00,
.TotalReportDescriptors = 1,
.HIDReportType = HID_DTYPE_Report,
.HIDReportLength = sizeof(XapReport)
},
.Xap_INEndpoint = {
.Header = {
.Size = sizeof(USB_Descriptor_Endpoint_t),
.Type = DTYPE_Endpoint
},
.EndpointAddress = (ENDPOINT_DIR_IN | XAP_IN_EPNUM),
.Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
.EndpointSize = XAP_EPSIZE,
.PollingIntervalMS = 0x01
},
.Xap_OUTEndpoint = {
.Header = {
.Size = sizeof(USB_Descriptor_Endpoint_t),
.Type = DTYPE_Endpoint
},
.EndpointAddress = (ENDPOINT_DIR_OUT | XAP_OUT_EPNUM),
.Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
.EndpointSize = XAP_EPSIZE,
.PollingIntervalMS = 0x01
},
#endif
#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
/*
* Mouse
@ -1161,6 +1235,14 @@ uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const
break;
#endif
#ifdef XAP_ENABLE
case XAP_INTERFACE:
Address = &ConfigurationDescriptor.Xap_HID;
Size = sizeof(USB_HID_Descriptor_HID_t);
break;
#endif
#ifdef CONSOLE_ENABLE
case CONSOLE_INTERFACE:
Address = &ConfigurationDescriptor.Console_HID;
@ -1218,6 +1300,14 @@ uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const
break;
#endif
#ifdef XAP_ENABLE
case XAP_INTERFACE:
Address = &XapReport;
Size = sizeof(XapReport);
break;
#endif
#ifdef CONSOLE_ENABLE
case CONSOLE_INTERFACE:
Address = &ConsoleReport;

View file

@ -75,6 +75,14 @@ typedef struct {
USB_Descriptor_Endpoint_t Raw_OUTEndpoint;
#endif
#ifdef XAP_ENABLE
// Mouse HID Interface
USB_Descriptor_Interface_t Xap_Interface;
USB_HID_Descriptor_HID_t Xap_HID;
USB_Descriptor_Endpoint_t Xap_INEndpoint;
USB_Descriptor_Endpoint_t Xap_OUTEndpoint;
#endif
#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
// Mouse HID Interface
USB_Descriptor_Interface_t Mouse_Interface;
@ -162,6 +170,10 @@ enum usb_interfaces {
RAW_INTERFACE,
#endif
#ifdef XAP_ENABLE
XAP_INTERFACE,
#endif
#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)
MOUSE_INTERFACE,
#endif
@ -223,6 +235,15 @@ enum usb_endpoints {
# endif
#endif
#ifdef XAP_ENABLE
XAP_IN_EPNUM = NEXT_EPNUM,
# if STM32_USB_USE_OTG1
# define XAP_OUT_EPNUM XAP_IN_EPNUM
# else
XAP_OUT_EPNUM = NEXT_EPNUM,
# endif
#endif
#ifdef SHARED_EP_ENABLE
SHARED_IN_EPNUM = NEXT_EPNUM,
#endif
@ -296,7 +317,7 @@ enum usb_endpoints {
// TODO - ARM_ATSAM
#if (NEXT_EPNUM - 1) > MAX_ENDPOINTS
# error There are not enough available endpoints to support all functions. Please disable one or more of the following: Mouse Keys, Extra Keys, Console, NKRO, MIDI, Serial, Steno
# error There are not enough available endpoints to support all functions. Please disable one or more of the following: Mouse Keys, Extra Keys, Console, NKRO, MIDI, Serial, Steno, XAP
#endif
#define KEYBOARD_EPSIZE 8
@ -309,5 +330,6 @@ enum usb_endpoints {
#define CDC_EPSIZE 16
#define JOYSTICK_EPSIZE 8
#define DIGITIZER_EPSIZE 8
#define XAP_EPSIZE 64
uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const void** const DescriptorAddress);

View file

@ -39,6 +39,10 @@ void console_task(void);
void raw_hid_task(void);
#endif
#ifdef XAP_ENABLE
void xap_task(void);
#endif
/* This is from main.c of USBaspLoader */
static void initForUsbConnectivity(void) {
uint8_t i = 0;
@ -163,6 +167,14 @@ void protocol_task(void) {
}
#endif
#ifdef XAP_ENABLE
usbPoll();
if (usbConfiguration && usbInterruptIsReady4()) {
xap_task();
}
#endif
#ifdef CONSOLE_ENABLE
usbPoll();

View file

@ -35,6 +35,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
# include "raw_hid.h"
#endif
#ifdef XAP_ENABLE
# include "xap.h"
# include <string.h>
#endif
#if defined(CONSOLE_ENABLE)
# define RBUF_SIZE 128
# include "ring_buffer.h"
@ -60,6 +65,10 @@ enum usb_interfaces {
RAW_INTERFACE = NEXT_INTERFACE,
#endif
#ifdef XAP_ENABLE
XAP_INTERFACE = NEXT_INTERFACE,
#endif
#if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP)
SHARED_INTERFACE = NEXT_INTERFACE,
#endif
@ -137,7 +146,7 @@ void raw_hid_send(uint8_t *data, uint8_t length) {
}
uint8_t *temp = data;
for (uint8_t i = 0; i < 4; i++) {
for (uint8_t i = 0; i < (RAW_BUFFER_SIZE / RAW_EPSIZE); i++) {
while (!usbInterruptIsReady4()) {
usbPoll();
}
@ -164,6 +173,85 @@ void raw_hid_task(void) {
}
#endif
/*------------------------------------------------------------------*
* XAP
*------------------------------------------------------------------*/
#ifdef XAP_ENABLE
# define XAP_BUFFER_SIZE 64
# define XAP_EPSIZE 8
static uint8_t xap_output_buffer[XAP_BUFFER_SIZE];
static uint8_t xap_output_received_bytes = 0;
extern void xap_receive(xap_token_t token, const uint8_t *data, size_t length);
void xap_send_base(uint8_t *data, uint8_t length) {
if (length != XAP_BUFFER_SIZE) {
return;
}
uint8_t *temp = data;
for (uint8_t i = 0; i < (XAP_BUFFER_SIZE / XAP_EPSIZE); i++) {
while (!usbInterruptIsReady4()) {
usbPoll();
}
usbSetInterrupt4(temp, 8);
temp += 8;
}
while (!usbInterruptIsReady4()) {
usbPoll();
}
usbSetInterrupt4(0, 0);
}
void xap_send(xap_token_t token, xap_response_flags_t response_flags, const void *data, size_t length) {
uint8_t rdata[XAP_BUFFER_SIZE] = {0};
xap_response_header_t *header = (xap_response_header_t *)&rdata[0];
header->token = token;
if (length > (XAP_BUFFER_SIZE - sizeof(xap_response_header_t))) response_flags &= ~(XAP_RESPONSE_FLAG_SUCCESS);
header->flags = response_flags;
if (response_flags & (XAP_RESPONSE_FLAG_SUCCESS)) {
header->length = (uint8_t)length;
if (data != NULL) {
memcpy(&rdata[sizeof(xap_response_header_t)], data, length);
}
}
xap_send_base(rdata, sizeof(rdata));
}
void xap_broadcast(uint8_t type, const void *data, size_t length) {
uint8_t rdata[XAP_BUFFER_SIZE] = {0};
xap_broadcast_header_t *header = (xap_broadcast_header_t *)&rdata[0];
header->token = XAP_BROADCAST_TOKEN;
header->type = type;
if (length > (XAP_BUFFER_SIZE - sizeof(xap_broadcast_header_t))) return;
header->length = (uint8_t)length;
if (data != NULL) {
memcpy(&rdata[sizeof(xap_broadcast_header_t)], data, length);
}
xap_send_base(rdata, sizeof(rdata));
}
void xap_receive_base(const void *data) {
const uint8_t * u8data = (const uint8_t *)data;
xap_request_header_t *header = (xap_request_header_t *)&u8data[0];
if (header->length <= (XAP_BUFFER_SIZE - sizeof(xap_request_header_t))) {
xap_receive(header->token, &u8data[sizeof(xap_request_header_t)], header->length);
}
}
void xap_task(void) {
if (xap_output_received_bytes == XAP_BUFFER_SIZE) {
xap_receive_base(xap_output_buffer);
xap_output_received_bytes = 0;
}
}
#endif
/*------------------------------------------------------------------*
* Console
*------------------------------------------------------------------*/
@ -402,6 +490,24 @@ void usbFunctionWriteOut(uchar *data, uchar len) {
raw_output_received_bytes += len;
}
#endif
#ifdef XAP_ENABLE
// Data from host must be divided every 8bytes
if (len != 8) {
dprint("XAP: invalid length\n");
xap_output_received_bytes = 0;
return;
}
if (xap_output_received_bytes + len > XAP_BUFFER_SIZE) {
dprint("XAP: buffer full\n");
xap_output_received_bytes = 0;
} else {
for (uint8_t i = 0; i < 8; i++) {
xap_output_buffer[xap_output_received_bytes + i] = data[i];
}
xap_output_received_bytes += len;
}
#endif
}
/*------------------------------------------------------------------*
@ -638,6 +744,29 @@ const PROGMEM uchar raw_hid_report[] = {
};
#endif
#ifdef XAP_ENABLE
const PROGMEM uchar xap_report[] = {
0x06, 0x51, 0xFF, // Usage Page (Vendor Defined)
0x09, 0x58, // Usage (Vendor Defined)
0xA1, 0x01, // Collection (Application)
// Data to host
0x09, 0x62, // Usage (Vendor Defined)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x95, XAP_BUFFER_SIZE, // Report Count
0x75, 0x08, // Report Size (8)
0x81, 0x02, // Input (Data, Variable, Absolute)
// Data from host
0x09, 0x63, // Usage (Vendor Defined)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x95, XAP_BUFFER_SIZE, // Report Count
0x75, 0x08, // Report Size (8)
0x91, 0x02, // Output (Data, Variable, Absolute)
0xC0 // End Collection
};
#endif
#if defined(CONSOLE_ENABLE)
const PROGMEM uchar console_hid_report[] = {
0x06, 0x31, 0xFF, // Usage Page (Vendor Defined - PJRC Teensy compatible)
@ -837,6 +966,56 @@ const PROGMEM usbConfigurationDescriptor_t usbConfigurationDescriptor = {
},
# endif
# if defined(XAP_ENABLE)
/*
* XAP
*/
.xapInterface = {
.header = {
.bLength = sizeof(usbInterfaceDescriptor_t),
.bDescriptorType = USBDESCR_INTERFACE
},
.bInterfaceNumber = XAP_INTERFACE,
.bAlternateSetting = 0x00,
.bNumEndpoints = 2,
.bInterfaceClass = 0x03,
.bInterfaceSubClass = 0x00,
.bInterfaceProtocol = 0x00,
.iInterface = 0x00
},
.xapHID = {
.header = {
.bLength = sizeof(usbHIDDescriptor_t),
.bDescriptorType = USBDESCR_HID
},
.bcdHID = 0x0101,
.bCountryCode = 0x00,
.bNumDescriptors = 1,
.bDescriptorType = USBDESCR_HID_REPORT,
.wDescriptorLength = sizeof(xap_report)
},
.xapINEndpoint = {
.header = {
.bLength = sizeof(usbEndpointDescriptor_t),
.bDescriptorType = USBDESCR_ENDPOINT
},
.bEndpointAddress = (USBRQ_DIR_DEVICE_TO_HOST | USB_CFG_EP4_NUMBER),
.bmAttributes = 0x03,
.wMaxPacketSize = XAP_EPSIZE,
.bInterval = USB_POLLING_INTERVAL_MS
},
.xapOUTEndpoint = {
.header = {
.bLength = sizeof(usbEndpointDescriptor_t),
.bDescriptorType = USBDESCR_ENDPOINT
},
.bEndpointAddress = (USBRQ_DIR_HOST_TO_DEVICE | USB_CFG_EP4_NUMBER),
.bmAttributes = 0x03,
.wMaxPacketSize = XAP_EPSIZE,
.bInterval = USB_POLLING_INTERVAL_MS
},
# endif
# ifdef SHARED_EP_ENABLE
/*
* Shared
@ -989,6 +1168,13 @@ USB_PUBLIC usbMsgLen_t usbFunctionDescriptor(struct usbRequest *rq) {
break;
#endif
#if defined(XAP_ENABLE)
case XAP_INTERFACE:
usbMsgPtr = (usbMsgPtr_t)&usbConfigurationDescriptor.xapHID;
len = sizeof(usbHIDDescriptor_t);
break;
#endif
#ifdef SHARED_EP_ENABLE
case SHARED_INTERFACE:
usbMsgPtr = (usbMsgPtr_t)&usbConfigurationDescriptor.sharedHID;
@ -1021,6 +1207,13 @@ USB_PUBLIC usbMsgLen_t usbFunctionDescriptor(struct usbRequest *rq) {
break;
#endif
#if defined(XAP_ENABLE)
case XAP_INTERFACE:
usbMsgPtr = (usbMsgPtr_t)xap_report;
len = sizeof(xap_report);
break;
#endif
#ifdef SHARED_EP_ENABLE
case SHARED_INTERFACE:
usbMsgPtr = (usbMsgPtr_t)shared_hid_report;

View file

@ -104,6 +104,13 @@ typedef struct usbConfigurationDescriptor {
usbEndpointDescriptor_t rawOUTEndpoint;
#endif
#if defined(XAP_ENABLE)
usbInterfaceDescriptor_t xapInterface;
usbHIDDescriptor_t xapHID;
usbEndpointDescriptor_t xapINEndpoint;
usbEndpointDescriptor_t xapOUTEndpoint;
#endif
#if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP)
usbInterfaceDescriptor_t sharedInterface;
usbHIDDescriptor_t sharedHID;