From eba91c6e286984459ea82ac9bce3e8bb91794764 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Wed, 11 Aug 2021 21:08:32 +1000 Subject: [PATCH 001/203] Initial implementation of XAP protocol. --- build_keyboard.mk | 24 ++ common_features.mk | 12 + data/xap/xap_0.0.1.hjson | 178 +++++++++++++ data/xap/xap_0.1.0.hjson | 248 ++++++++++++++++++ data/xap/xap_0.2.0.hjson | 80 ++++++ docs/xap_0.0.1.md | 62 +++++ docs/xap_0.1.0.md | 78 ++++++ docs/xap_0.2.0.md | 78 ++++++ docs/xap_protocol.md | 5 + lib/python/qmk/casing.py | 65 +++++ lib/python/qmk/cli/__init__.py | 3 + lib/python/qmk/cli/xap/__init__.py | 0 lib/python/qmk/cli/xap/generate_docs.py | 11 + lib/python/qmk/cli/xap/generate_json.py | 13 + lib/python/qmk/cli/xap/generate_qmk.py | 24 ++ lib/python/qmk/commands.py | 7 +- lib/python/qmk/constants.py | 90 +++++++ lib/python/qmk/xap/__init__.py | 0 lib/python/qmk/xap/common.py | 81 ++++++ lib/python/qmk/xap/gen_client_js/__init__.py | 0 lib/python/qmk/xap/gen_docs/__init__.py | 0 lib/python/qmk/xap/gen_docs/generator.py | 103 ++++++++ lib/python/qmk/xap/gen_firmware/__init__.py | 0 .../qmk/xap/gen_firmware/header_generator.py | 136 ++++++++++ .../qmk/xap/gen_firmware/inline_generator.py | 222 ++++++++++++++++ quantum/quantum.h | 4 + quantum/xap/xap.c | 116 ++++++++ quantum/xap/xap.h | 44 ++++ quantum/xap/xap_handlers.c | 39 +++ requirements.txt | 1 + tmk_core/common/report.h | 3 +- tmk_core/protocol/lufa/lufa.c | 96 +++++++ tmk_core/protocol/usb_descriptor.c | 91 +++++++ tmk_core/protocol/usb_descriptor.h | 24 +- 34 files changed, 1934 insertions(+), 4 deletions(-) create mode 100755 data/xap/xap_0.0.1.hjson create mode 100755 data/xap/xap_0.1.0.hjson create mode 100755 data/xap/xap_0.2.0.hjson create mode 100644 docs/xap_0.0.1.md create mode 100644 docs/xap_0.1.0.md create mode 100644 docs/xap_0.2.0.md create mode 100644 docs/xap_protocol.md create mode 100755 lib/python/qmk/casing.py create mode 100755 lib/python/qmk/cli/xap/__init__.py create mode 100755 lib/python/qmk/cli/xap/generate_docs.py create mode 100755 lib/python/qmk/cli/xap/generate_json.py create mode 100755 lib/python/qmk/cli/xap/generate_qmk.py create mode 100644 lib/python/qmk/xap/__init__.py create mode 100755 lib/python/qmk/xap/common.py create mode 100644 lib/python/qmk/xap/gen_client_js/__init__.py create mode 100644 lib/python/qmk/xap/gen_docs/__init__.py create mode 100755 lib/python/qmk/xap/gen_docs/generator.py create mode 100644 lib/python/qmk/xap/gen_firmware/__init__.py create mode 100755 lib/python/qmk/xap/gen_firmware/header_generator.py create mode 100755 lib/python/qmk/xap/gen_firmware/inline_generator.py create mode 100644 quantum/xap/xap.c create mode 100644 quantum/xap/xap.h create mode 100644 quantum/xap/xap_handlers.c diff --git a/build_keyboard.mk b/build_keyboard.mk index 38ca2aaa99be..79bd87326ca6 100644 --- a/build_keyboard.mk +++ b/build_keyboard.mk @@ -352,6 +352,30 @@ VPATH += $(KEYBOARD_PATHS) VPATH += $(COMMON_VPATH) include common_features.mk + +# XAP embedded info.json +ifeq ($(strip $(XAP_ENABLE)), yes) + +$(KEYMAP_OUTPUT)/src/info_json_gz.h: $(INFO_JSON_FILES) + mkdir -p $(KEYMAP_OUTPUT)/src + $(QMK_BIN) info -f json -kb $(KEYBOARD) -km $(KEYMAP) | gzip -c9 > $(KEYMAP_OUTPUT)/src/info.json.gz + cd $(KEYMAP_OUTPUT)/src >/dev/null 2>&1 \ + && xxd -i info.json.gz info_json_gz.h \ + && cd - >/dev/null 2>&1 + +XAP_FILES := $(shell ls -1 data/xap/* | sort | xargs echo) + +$(KEYMAP_OUTPUT)/src/xap_generated.inl: $(XAP_FILES) + $(QMK_BIN) xap-generate-qmk-inc -o "$(KEYMAP_OUTPUT)/src/xap_generated.inl" + +$(KEYMAP_OUTPUT)/src/xap_generated.h: $(XAP_FILES) + $(QMK_BIN) xap-generate-qmk-h -o "$(KEYMAP_OUTPUT)/src/xap_generated.h" -kb $(KEYBOARD) + +generated-files: $(KEYMAP_OUTPUT)/src/info_json_gz.h $(KEYMAP_OUTPUT)/src/xap_generated.inl $(KEYMAP_OUTPUT)/src/xap_generated.h + +VPATH += $(KEYMAP_OUTPUT)/src +endif + include $(TMK_PATH)/protocol.mk include $(TMK_PATH)/common.mk include bootloader.mk diff --git a/common_features.mk b/common_features.mk index e442222eae5c..9b5cfc55205a 100644 --- a/common_features.mk +++ b/common_features.mk @@ -718,3 +718,15 @@ ifeq ($(strip $(USBPD_ENABLE)), yes) endif 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 + DYNAMIC_KEYMAP_ENABLE := yes + EMBED_INFO_JSON := yes + VPATH += $(QUANTUM_DIR)/xap + SRC += $(QUANTUM_DIR)/xap/xap.c $(QUANTUM_DIR)/xap/xap_handlers.c +endif diff --git a/data/xap/xap_0.0.1.hjson b/data/xap/xap_0.0.1.hjson new file mode 100755 index 000000000000..34fd21d62370 --- /dev/null +++ b/data/xap/xap_0.0.1.hjson @@ -0,0 +1,178 @@ +{ + 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! + term_definitions + !term_definitions! + request_response + reserved_tokens + response_flags + !response_flags! + example_conversation + ] + + 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, allowing repsonse 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. + + 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` | + ''' + } + + 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. + ''' + ID: + ''' + A single octet / 8-bit byte. + ''' + 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. + ''' + Token: + ''' + A `u16` associated with a specific request as well as its corresponding response. + ''' + Response: + ''' + The data sent back to the host during execution of a _handler_. + ''' + "Response Flags": + ''' + An `u8` containing the status of the request. + ''' + Payload: + ''' + Any received data appended to the _route_, which gets delivered to the _handler_ when received. + ''' + } + + response_flags: { + define_prefix: XAP_RESP + 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}. + * Response: + * `u32` value. + ''' + return_type: u32 + return_purpose: bcd-version + return_constant: XAP_BCD_VERSION + } + } + } + } +} diff --git a/data/xap/xap_0.1.0.hjson b/data/xap/xap_0.1.0.hjson new file mode 100755 index 000000000000..e24978f459ab --- /dev/null +++ b/data/xap/xap_0.1.0.hjson @@ -0,0 +1,248 @@ +{ + version: 0.1.0 + + documentation: { + order: [ + broadcast_messages + ] + + reserved_tokens: + ''' + Two token values are reserved: `0x0000` and `0xFFFF`: + * `0x0000`: 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 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_. + ''' + } + + 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`( ) | `0x51`(Q) | `0x4D`(M) | `0x4B`(K) | `0x21`(!) | + ''' + } + } + } + + 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 + } + } + }, + + 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}. + * Response: + * `u32` value. + ''' + 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_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) + } + } + }, + + 0x02: { + type: router + name: Keyboard + define: KB + description: + ''' + This subsystem is always present, and reserved for user-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: { + } + } + } +} diff --git a/data/xap/xap_0.2.0.hjson b/data/xap/xap_0.2.0.hjson new file mode 100755 index 000000000000..81c518739046 --- /dev/null +++ b/data/xap/xap_0.2.0.hjson @@ -0,0 +1,80 @@ +{ + version: 0.2.0 + + routes: { + 0x04: { + type: router + name: Dynamic Keymap + define: DYNAMIC_KEYMAP + description: + ''' + This subsystem allows for live modifications of the keymap, allowing keys to be reassigned without rebuilding the firmware. + ''' + enable_if_preprocessor: defined(DYNAMIC_KEYMAP_ENABLE) + routes: { + 0x00: { + type: command + name: Capabilities Query + define: CAPABILITIES_QUERY + description: + ''' + Dynamic 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_DYNAMIC_KEYMAP_CAPABILITIES + } + } + } + + 0x05: { + type: router + name: Dynamic Encoders + define: DYNAMIC_ENCODER + description: + ''' + This subsystem allows for live modifications of the keymap, allowing encoder functionality to be reassigned without rebuilding the firmware. + ''' + enable_if_preprocessor: defined(DYNAMIC_KEYMAP_ENABLE) && defined(ENCODER_MAP_ENABLE) + routes: { + 0x00: { + type: command + name: Capabilities Query + define: CAPABILITIES_QUERY + description: + ''' + Dynamic Encoders 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_DYNAMIC_ENCODER_CAPABILITIES + } + } + } + + 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: { + 0x00: { + 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 + } + } + } + } +} diff --git a/docs/xap_0.0.1.md b/docs/xap_0.0.1.md new file mode 100644 index 000000000000..ff45f1ae4500 --- /dev/null +++ b/docs/xap_0.0.1.md @@ -0,0 +1,62 @@ +# 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. | +| _ID_ | A single octet / 8-bit byte. | +| _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_. | +| _Response Flags_ | An `u8` containing the status of the request. | +| _Route_ | A sequence of _IDs_ describing the route to invoke a _handler_. | +| _Subsystem_ | A high-level area of functionality within XAP. | +| _Token_ | A `u16` associated with a specific request as well as its corresponding 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, allowing repsonse 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. + +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`: 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` | + diff --git a/docs/xap_0.1.0.md b/docs/xap_0.1.0.md new file mode 100644 index 000000000000..8dac167209fd --- /dev/null +++ b/docs/xap_0.1.0.md @@ -0,0 +1,78 @@ +# 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 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. | +| _ID_ | A single octet / 8-bit byte. | +| _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_. | +| _Response Flags_ | An `u8` containing the status of the request. | +| _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. | +| _Token_ | A `u16` associated with a specific request as well as its corresponding response. | +| _Unlock sequence_ | A physical sequence initiated by the user to enable execution of _secure routes_. | + +## 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, allowing repsonse 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. + +This token is followed by a `u8` signifying the length of data in the request. + +Two token values are reserved: `0x0000` and `0xFFFF`: +* `0x0000`: 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 | Unlocking | - | - | - | - | Secure Failure | Success | + +* `Bit 7`: When this bit is set, an _unlock sequence_ has completed, and _secure routes_ may be invoked. +* `Bit 6`: When this bit is set, an _unlock sequence_ is in progress. +* `Bit 1`: When this bit is set, the requested _route_ was marked _secure_ but an _unlock sequence_ has not completed. +* `Bit 0`: 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` | + +## 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_. + diff --git a/docs/xap_0.2.0.md b/docs/xap_0.2.0.md new file mode 100644 index 000000000000..8dac167209fd --- /dev/null +++ b/docs/xap_0.2.0.md @@ -0,0 +1,78 @@ +# 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 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. | +| _ID_ | A single octet / 8-bit byte. | +| _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_. | +| _Response Flags_ | An `u8` containing the status of the request. | +| _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. | +| _Token_ | A `u16` associated with a specific request as well as its corresponding response. | +| _Unlock sequence_ | A physical sequence initiated by the user to enable execution of _secure routes_. | + +## 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, allowing repsonse 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. + +This token is followed by a `u8` signifying the length of data in the request. + +Two token values are reserved: `0x0000` and `0xFFFF`: +* `0x0000`: 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 | Unlocking | - | - | - | - | Secure Failure | Success | + +* `Bit 7`: When this bit is set, an _unlock sequence_ has completed, and _secure routes_ may be invoked. +* `Bit 6`: When this bit is set, an _unlock sequence_ is in progress. +* `Bit 1`: When this bit is set, the requested _route_ was marked _secure_ but an _unlock sequence_ has not completed. +* `Bit 0`: 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` | + +## 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_. + diff --git a/docs/xap_protocol.md b/docs/xap_protocol.md new file mode 100644 index 000000000000..688d7dab3ea4 --- /dev/null +++ b/docs/xap_protocol.md @@ -0,0 +1,5 @@ +# XAP Protocol Reference + +* [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) diff --git a/lib/python/qmk/casing.py b/lib/python/qmk/casing.py new file mode 100755 index 000000000000..60b14cc5401d --- /dev/null +++ b/lib/python/qmk/casing.py @@ -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)]) diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py index dea0eaeaf939..fbad3b9329de 100644 --- a/lib/python/qmk/cli/__init__.py +++ b/lib/python/qmk/cli/__init__.py @@ -66,6 +66,9 @@ 'qmk.cli.new.keymap', 'qmk.cli.pyformat', 'qmk.cli.pytest', + 'qmk.cli.xap.generate_docs', + 'qmk.cli.xap.generate_json', + 'qmk.cli.xap.generate_qmk', ] diff --git a/lib/python/qmk/cli/xap/__init__.py b/lib/python/qmk/cli/xap/__init__.py new file mode 100755 index 000000000000..e69de29bb2d1 diff --git a/lib/python/qmk/cli/xap/generate_docs.py b/lib/python/qmk/cli/xap/generate_docs.py new file mode 100755 index 000000000000..bf2fdb86bbb9 --- /dev/null +++ b/lib/python/qmk/cli/xap/generate_docs.py @@ -0,0 +1,11 @@ +"""This script generates the XAP protocol documentation. +""" +from milc import cli +from qmk.xap.gen_docs.generator import generate_docs + + +@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/`. + """ + generate_docs() \ No newline at end of file diff --git a/lib/python/qmk/cli/xap/generate_json.py b/lib/python/qmk/cli/xap/generate_json.py new file mode 100755 index 000000000000..e999f6ce87dd --- /dev/null +++ b/lib/python/qmk/cli/xap/generate_json.py @@ -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)) diff --git a/lib/python/qmk/cli/xap/generate_qmk.py b/lib/python/qmk/cli/xap/generate_qmk.py new file mode 100755 index 000000000000..0cb2fec1954a --- /dev/null +++ b/lib/python/qmk/cli/xap/generate_qmk.py @@ -0,0 +1,24 @@ +"""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.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.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. + """ + generate_inline(cli.args.output) + + +@cli.argument('-o', '--output', type=normpath, help='File to write to') +@cli.argument('-kb', '--keyboard', help='Name of the keyboard') +@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. + """ + generate_header(cli.args.output, cli.args.keyboard) diff --git a/lib/python/qmk/commands.py b/lib/python/qmk/commands.py index 8c66228b2b7b..58d7a381d687 100644 --- a/lib/python/qmk/commands.py +++ b/lib/python/qmk/commands.py @@ -87,11 +87,14 @@ def create_make_command(keyboard, keymap, target=None, parallel=1, **env_vars): return create_make_target(':'.join(make_args), parallel, **env_vars) -def get_git_version(current_time, repo_dir='.', check_dir='.'): +def get_git_version(current_time=None, repo_dir='.', check_dir='.'): """Returns the current git version for a repo, or the current time. """ git_describe_cmd = ['git', 'describe', '--abbrev=6', '--dirty', '--always', '--tags'] + if current_time is None: + current_time = strftime(time_fmt) + if repo_dir != '.': repo_dir = Path('lib') / repo_dir @@ -118,7 +121,7 @@ def create_version_h(skip_git=False, skip_all=False): if skip_all: current_time = "1970-01-01-00:00:00" else: - current_time = strftime(time_fmt) + current_time = None if skip_git: git_version = "NA" diff --git a/lib/python/qmk/constants.py b/lib/python/qmk/constants.py index 71a6c91c7784..c4bfc92a5f0d 100644 --- a/lib/python/qmk/constants.py +++ b/lib/python/qmk/constants.py @@ -1,6 +1,7 @@ """Information that should be available to the python library. """ from os import environ +from datetime import date from pathlib import Path # The root of the qmk_firmware tree. @@ -36,3 +37,92 @@ # Constants that should match their counterparts in make BUILD_DIR = environ.get('BUILD_DIR', '.build') KEYBOARD_OUTPUT_PREFIX = f'{BUILD_DIR}/obj_' + +# Headers for generated files +this_year = date.today().year +GPL2_HEADER_C_LIKE = f'''\ +/* Copyright {this_year} QMK + * + * 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 . + */ +''' + +GPL2_HEADER_SH_LIKE = f'''\ +# Copyright {this_year} QMK +# +# 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 . +''' + +GENERATED_HEADER_C_LIKE = '''\ +/******************************************************************************* + 88888888888 888 d8b .d888 d8b 888 d8b + 888 888 Y8P d88P" Y8P 888 Y8P + 888 888 888 888 + 888 88888b. 888 .d8888b 888888 888 888 .d88b. 888 .d8888b + 888 888 "88b 888 88K 888 888 888 d8P Y8b 888 88K + 888 888 888 888 "Y8888b. 888 888 888 88888888 888 "Y8888b. + 888 888 888 888 X88 888 888 888 Y8b. 888 X88 + 888 888 888 888 88888P' 888 888 888 "Y8888 888 88888P' + + 888 888 + 888 888 + 888 888 + .d88b. .d88b. 88888b. .d88b. 888d888 8888b. 888888 .d88b. .d88888 + d88P"88b d8P Y8b 888 "88b d8P Y8b 888P" "88b 888 d8P Y8b d88" 888 + 888 888 88888888 888 888 88888888 888 .d888888 888 88888888 888 888 + Y88b 888 Y8b. 888 888 Y8b. 888 888 888 Y88b. Y8b. Y88b 888 + "Y88888 "Y8888 888 888 "Y8888 888 "Y888888 "Y888 "Y8888 "Y88888 + 888 + Y8b d88P + "Y88P" +*******************************************************************************/ +''' + +GENERATED_HEADER_SH_LIKE = '''\ +################################################################################ +# +# 88888888888 888 d8b .d888 d8b 888 d8b +# 888 888 Y8P d88P" Y8P 888 Y8P +# 888 888 888 888 +# 888 88888b. 888 .d8888b 888888 888 888 .d88b. 888 .d8888b +# 888 888 "88b 888 88K 888 888 888 d8P Y8b 888 88K +# 888 888 888 888 "Y8888b. 888 888 888 88888888 888 "Y8888b. +# 888 888 888 888 X88 888 888 888 Y8b. 888 X88 +# 888 888 888 888 88888P' 888 888 888 "Y8888 888 88888P' +# +# 888 888 +# 888 888 +# 888 888 +# .d88b. .d88b. 88888b. .d88b. 888d888 8888b. 888888 .d88b. .d88888 +# d88P"88b d8P Y8b 888 "88b d8P Y8b 888P" "88b 888 d8P Y8b d88" 888 +# 888 888 88888888 888 888 88888888 888 .d888888 888 88888888 888 888 +# Y88b 888 Y8b. 888 888 Y8b. 888 888 888 Y88b. Y8b. Y88b 888 +# "Y88888 "Y8888 888 888 "Y8888 888 "Y888888 "Y888 "Y8888 "Y88888 +# 888 +# Y8b d88P +# "Y88P" +# +################################################################################ +''' diff --git a/lib/python/qmk/xap/__init__.py b/lib/python/qmk/xap/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/lib/python/qmk/xap/common.py b/lib/python/qmk/xap/common.py new file mode 100755 index 000000000000..bec82d727bb9 --- /dev/null +++ b/lib/python/qmk/xap/common.py @@ -0,0 +1,81 @@ +"""This script handles the XAP protocol data files. +""" +import re +import hjson +from typing import OrderedDict + +from qmk.constants import QMK_FIRMWARE + + +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): + 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 /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]) + + +def latest_xap_defs(): + """Gets the latest version of the XAP definitions. + """ + definitions = [hjson.load(file.open(encoding='utf-8')) for file in get_xap_definition_files()] + return _merge_ordered_dicts(definitions) + + +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]) + ")" diff --git a/lib/python/qmk/xap/gen_client_js/__init__.py b/lib/python/qmk/xap/gen_client_js/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/lib/python/qmk/xap/gen_docs/__init__.py b/lib/python/qmk/xap/gen_docs/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/lib/python/qmk/xap/gen_docs/generator.py b/lib/python/qmk/xap/gen_docs/generator.py new file mode 100755 index 000000000000..3e0ce5289624 --- /dev/null +++ b/lib/python/qmk/xap/gen_docs/generator.py @@ -0,0 +1,103 @@ +"""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 + + +def _update_type_docs(overall): + defs = overall['type_docs'] + + type_docs = [] + for (k, v) in sorted(defs.items(), key=lambda x: x[0]): + type_docs.append(f'| _{k}_ | {v} |') + + desc_str = "\n".join(type_docs) + + overall['documentation']['!type_docs!'] = f'''\ +| Name | Definition | +| -- | -- | +{desc_str} +''' + + +def _update_term_definitions(overall): + defs = overall['term_definitions'] + + term_descriptions = [] + for (k, v) in sorted(defs.items(), key=lambda x: x[0]): + term_descriptions.append(f'| _{k}_ | {v} |') + + desc_str = "\n".join(term_descriptions) + + overall['documentation']['!term_definitions!'] = f'''\ +| Name | Definition | +| -- | -- | +{desc_str} +''' + + +def _update_response_flags(overall): + flags = overall['response_flags']['bits'] + for n in range(0, 8): + if str(n) not in flags: + flags[str(n)] = {"name": "-", "description": "-"} + + header = '| ' + " | ".join([f'Bit {n}' for n in range(7, -1, -1)]) + ' |' + dividers = '|' + "|".join(['--' for n in range(7, -1, -1)]) + '|' + bit_names = '| ' + " | ".join([flags[str(n)]['name'] for n in range(7, -1, -1)]) + ' |' + + bit_descriptions = '' + for n in range(7, -1, -1): + bit_desc = flags[str(n)] + if bit_desc['name'] != '-': + desc = bit_desc['description'] + bit_descriptions = bit_descriptions + f'\n* `Bit {n}`: {desc}' + + overall['documentation']['!response_flags!'] = f'''\ +{header} +{dividers} +{bit_names} +{bit_descriptions} +''' + + +def generate_docs(): + """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'))) + + try: + if 'type_docs' in overall: + _update_type_docs(overall) + if 'term_definitions' in overall: + _update_term_definitions(overall) + if 'response_flags' in overall: + _update_response_flags(overall) + except: + print(hjson.dumps(overall)) + exit(1) + + output_doc = QMK_FIRMWARE / "docs" / f"{file.stem}.md" + docs_list.append(output_doc) + + with open(output_doc, "w", encoding='utf-8') as out_file: + for e in overall['documentation']['order']: + out_file.write(overall['documentation'][e].strip()) + out_file.write('\n\n') + + output_doc = QMK_FIRMWARE / "docs" / f"xap_protocol.md" + with open(output_doc, "w", encoding='utf-8') as out_file: + out_file.write('''\ +# XAP Protocol Reference + +''') + + for file in reversed(sorted(docs_list)): + ver = file.stem[4:] + out_file.write(f'* [XAP Version {ver}]({file.name})\n') diff --git a/lib/python/qmk/xap/gen_firmware/__init__.py b/lib/python/qmk/xap/gen_firmware/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/lib/python/qmk/xap/gen_firmware/header_generator.py b/lib/python/qmk/xap/gen_firmware/header_generator.py new file mode 100755 index 000000000000..9b295ed8f3cd --- /dev/null +++ b/lib/python/qmk/xap/gen_firmware/header_generator.py @@ -0,0 +1,136 @@ +"""This script generates the XAP protocol generated header to be compiled into QMK. +""" +import re +import pyhash + +from qmk.commands import get_git_version +from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE +from qmk.xap.common import latest_xap_defs, route_conditions + + +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 generate_header(output_file, keyboard): + """Generates the XAP protocol header file, generated during normal build. + """ + xap_defs = latest_xap_defs() + + # 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.match(get_git_version()) + lines.append(f'#define QMK_BCD_VERSION 0x{int(b.group(1)):02d}{int(b.group(2)):02d}{int(b.group(3)):04d}ul') + keyboard_id = pyhash.murmur3_32()(keyboard) + lines.append(f'#define XAP_KEYBOARD_IDENTIFIER 0x{keyboard_id:08X}ul') + 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('') + + # Generate the full output + xap_generated_inl = '\n'.join(lines) + + # Clean up newlines + while "\n\n\n" in xap_generated_inl: + xap_generated_inl = xap_generated_inl.replace("\n\n\n", "\n\n") + + if output_file: + if output_file.name == '-': + print(xap_generated_inl) + else: + output_file.parent.mkdir(parents=True, exist_ok=True) + if output_file.exists(): + output_file.replace(output_file.parent / (output_file.name + '.bak')) + output_file.write_text(xap_generated_inl) diff --git a/lib/python/qmk/xap/gen_firmware/inline_generator.py b/lib/python/qmk/xap/gen_firmware/inline_generator.py new file mode 100755 index 000000000000..756b0e866642 --- /dev/null +++ b/lib/python/qmk/xap/gen_firmware/inline_generator.py @@ -0,0 +1,222 @@ +"""This script generates the XAP protocol generated header to be compiled into QMK. +""" +import pyhash + +from qmk.casing import to_snake +from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE +from qmk.xap.common import latest_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_route_type(container): + if 'routes' in container: + return 'XAP_ROUTE' + elif 'return_constant' in container: + if container['return_type'] == 'u32': + return 'XAP_VALUE' + 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_constant' in container: + + if container['return_type'] == 'u32': + pass + + elif container['return_type'] == 'struct': + lines.append('') + lines.append(f'static const struct {route_name}_t {{') + + for member in container['return_struct_members']: + member_type = _get_c_type(member['type']) + member_name = to_snake(member['name']) + lines.append(f' const {member_type} {member_name};') + + lines.append(f'}} {route_name}_data PROGMEM = {{') + + for constant in container['return_constant']: + lines.append(f' {constant},') + + lines.append(f'}};') + + 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): + is_secure = 1 if ('secure' in container and container['secure'] is True) else 0 + lines.append(f' .flags = {{') + lines.append(f' .type = {_get_route_type(container)},') + lines.append(f' .is_secure = {is_secure},') + lines.append(f' }},') + + +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_u32value(lines, container, container_id, route_stack): + value = container['return_constant'] + lines.append(f' .u32value = {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_constant' in container: + if container['return_type'] == 'u32': + _append_routing_table_entry_u32value(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(f' }},') + + 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 generate_inline(output_file): + """Generates the XAP protocol header file, generated during normal build. + """ + xap_defs = latest_xap_defs() + + # Preamble + lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, ''] + + # Add all the generated code + _append_routing_tables(lines, xap_defs) + + # Generate the full output + xap_generated_inl = '\n'.join(lines) + + # Clean up newlines + while "\n\n\n" in xap_generated_inl: + xap_generated_inl = xap_generated_inl.replace("\n\n\n", "\n\n") + + if output_file: + if output_file.name == '-': + print(xap_generated_inl) + else: + output_file.parent.mkdir(parents=True, exist_ok=True) + if output_file.exists(): + output_file.replace(output_file.parent / (output_file.name + '.bak')) + output_file.write_text(xap_generated_inl) diff --git a/quantum/quantum.h b/quantum/quantum.h index ffb5e0df45df..d9114a0aef2e 100644 --- a/quantum/quantum.h +++ b/quantum/quantum.h @@ -184,6 +184,10 @@ extern layer_state_t layer_state; # include "dynamic_keymap.h" #endif +#ifdef XAP_ENABLE +# include "xap.h" +#endif + #ifdef VIA_ENABLE # include "via.h" #endif diff --git a/quantum/xap/xap.c b/quantum/xap/xap.c new file mode 100644 index 000000000000..df25f799d596 --- /dev/null +++ b/quantum/xap/xap.c @@ -0,0 +1,116 @@ +/* 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 . + */ + +#include +#include +#include + +#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 struct __attribute__((packed)) xap_route_flags_t { + xap_route_type_t type : XAP_ROUTE_TYPE_BIT_COUNT; + uint8_t is_secure : 1; +} 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_VALUE + uint32_t u32value; + + // XAP_GETTER + uint32_t (*u32getter)(void); + + // XAP_CONST_MEM + struct { + const void * const_data; + const uint8_t const_data_len; + }; + }; +}; + +#include + +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)); + 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_VALUE: + xap_respond_u32(token, route.u32value); + return; + + case XAP_GETTER: + xap_respond_u32(token, (route.u32getter)()); + 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); } diff --git a/quantum/xap/xap.h b/quantum/xap/xap.h new file mode 100644 index 000000000000..f3cab6aef29b --- /dev/null +++ b/quantum/xap/xap.h @@ -0,0 +1,44 @@ +/* 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 . + */ + +#pragma once + +#include +#include + +typedef uint8_t xap_identifier_t; +typedef uint8_t xap_response_flags_t; +typedef uint16_t xap_token_t; + +#ifndef XAP_SUBSYSTEM_VERSION_KB +# define XAP_SUBSYSTEM_VERSION_KB 0 +#endif + +#ifndef XAP_SUBSYSTEM_VERSION_USER +# define XAP_SUBSYSTEM_VERSION_USER 0 +#endif + +#define XAP_RESPONSE_FLAG_FAILED 0 +#define XAP_RESPONSE_FLAG_SUCCESS (1 << 0) + +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); + +#include diff --git a/quantum/xap/xap_handlers.c b/quantum/xap/xap_handlers.c new file mode 100644 index 000000000000..7c3d8127ce91 --- /dev/null +++ b/quantum/xap/xap_handlers.c @@ -0,0 +1,39 @@ +/* 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 . + */ + +#include +#include +#include + +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; } diff --git a/requirements.txt b/requirements.txt index 92381d7d5182..079dcdb8acc2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,5 +7,6 @@ hjson jsonschema>=3 milc>=1.4.2 pygments +pyhash pyusb qmk-dotty-dict diff --git a/tmk_core/common/report.h b/tmk_core/common/report.h index db6370657d53..cf9f1df58906 100644 --- a/tmk_core/common/report.h +++ b/tmk_core/common/report.h @@ -30,7 +30,8 @@ enum hid_report_ids { REPORT_ID_SYSTEM, REPORT_ID_CONSUMER, REPORT_ID_NKRO, - REPORT_ID_JOYSTICK + REPORT_ID_JOYSTICK, + REPORT_ID_XAP }; /* Mouse buttons */ diff --git a/tmk_core/protocol/lufa/lufa.c b/tmk_core/protocol/lufa/lufa.c index 4ac079e16872..16ad71414c6a 100644 --- a/tmk_core/protocol/lufa/lufa.c +++ b/tmk_core/protocol/lufa/lufa.c @@ -85,6 +85,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 @@ -249,6 +253,88 @@ 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, uint8_t response_flags, const void *data, size_t length) { + uint8_t rdata[XAP_EPSIZE] = {0}; + *(xap_token_t *)&rdata[0] = token; + if (length > (XAP_EPSIZE - 4)) response_flags &= ~(XAP_RESPONSE_FLAG_SUCCESS); + rdata[2] = response_flags; + if (response_flags & (XAP_RESPONSE_FLAG_SUCCESS)) { + rdata[3] = (uint8_t)length; + if (data != NULL) { + memcpy(&rdata[4], data, length); + } + } + xap_send_base(rdata, sizeof(rdata)); +} + +void xap_receive_base(const void *data) { + const uint8_t *u8data = (const uint8_t *)data; + xap_token_t token = *(xap_token_t *)&u8data[0]; + uint8_t length = u8data[2]; + if (length <= (XAP_EPSIZE - 3)) { + xap_receive(token, &u8data[3], 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 ******************************************************************************/ @@ -500,6 +586,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); @@ -1102,6 +1194,10 @@ int main(void) { raw_hid_task(); #endif +#ifdef XAP_ENABLE + xap_task(); +#endif + #if !defined(INTERRUPT_CONTROL_ENDPOINT) USB_USBTask(); #endif diff --git a/tmk_core/protocol/usb_descriptor.c b/tmk_core/protocol/usb_descriptor.c index 7a4a79031545..0a15e0c6d1de 100644 --- a/tmk_core/protocol/usb_descriptor.c +++ b/tmk_core/protocol/usb_descriptor.c @@ -227,6 +227,7 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = { HID_RI_OUTPUT(8, HID_IOF_CONSTANT), HID_RI_END_COLLECTION(0), #endif + #ifdef SHARED_EP_ENABLE }; #endif @@ -255,6 +256,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) @@ -492,6 +517,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 @@ -1046,6 +1121,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; @@ -1096,6 +1179,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; diff --git a/tmk_core/protocol/usb_descriptor.h b/tmk_core/protocol/usb_descriptor.h index 867e549b4f84..e7afe68640c1 100644 --- a/tmk_core/protocol/usb_descriptor.h +++ b/tmk_core/protocol/usb_descriptor.h @@ -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; @@ -155,6 +163,10 @@ enum usb_interfaces { RAW_INTERFACE, #endif +#ifdef XAP_ENABLE + XAP_INTERFACE, +#endif + #if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP) MOUSE_INTERFACE, #endif @@ -212,6 +224,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 @@ -272,7 +293,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 @@ -284,5 +305,6 @@ enum usb_endpoints { #define CDC_NOTIFICATION_EPSIZE 8 #define CDC_EPSIZE 16 #define JOYSTICK_EPSIZE 8 +#define XAP_EPSIZE 64 uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const void** const DescriptorAddress); From 437559cd037a97103b5d898ddfe40d0145cbfc59 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Wed, 15 Sep 2021 08:45:14 +1000 Subject: [PATCH 002/203] Swap to fnvhash due to deps. --- lib/python/qmk/xap/gen_firmware/header_generator.py | 4 ++-- requirements.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/python/qmk/xap/gen_firmware/header_generator.py b/lib/python/qmk/xap/gen_firmware/header_generator.py index 9b295ed8f3cd..66f01d185584 100755 --- a/lib/python/qmk/xap/gen_firmware/header_generator.py +++ b/lib/python/qmk/xap/gen_firmware/header_generator.py @@ -1,7 +1,7 @@ """This script generates the XAP protocol generated header to be compiled into QMK. """ import re -import pyhash +from fnvhash import fnv1a_32 from qmk.commands import get_git_version from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE @@ -107,7 +107,7 @@ def generate_header(output_file, keyboard): 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.match(get_git_version()) lines.append(f'#define QMK_BCD_VERSION 0x{int(b.group(1)):02d}{int(b.group(2)):02d}{int(b.group(3)):04d}ul') - keyboard_id = pyhash.murmur3_32()(keyboard) + keyboard_id = fnv1a_32(keyboard) lines.append(f'#define XAP_KEYBOARD_IDENTIFIER 0x{keyboard_id:08X}ul') lines.append('') diff --git a/requirements.txt b/requirements.txt index 079dcdb8acc2..6357b4e2eb0d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,11 +2,11 @@ appdirs argcomplete colorama +fnvhash hid hjson jsonschema>=3 milc>=1.4.2 pygments -pyhash pyusb qmk-dotty-dict From 5aae5a767f0659649b629cd7c8184ed58fb1c7b8 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Wed, 15 Sep 2021 08:49:51 +1000 Subject: [PATCH 003/203] Use the correct input type. --- lib/python/qmk/xap/gen_firmware/header_generator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/python/qmk/xap/gen_firmware/header_generator.py b/lib/python/qmk/xap/gen_firmware/header_generator.py index 66f01d185584..24dc8d34508e 100755 --- a/lib/python/qmk/xap/gen_firmware/header_generator.py +++ b/lib/python/qmk/xap/gen_firmware/header_generator.py @@ -107,7 +107,7 @@ def generate_header(output_file, keyboard): 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.match(get_git_version()) lines.append(f'#define QMK_BCD_VERSION 0x{int(b.group(1)):02d}{int(b.group(2)):02d}{int(b.group(3)):04d}ul') - keyboard_id = fnv1a_32(keyboard) + keyboard_id = fnv1a_32(bytes(keyboard, 'utf-8')) lines.append(f'#define XAP_KEYBOARD_IDENTIFIER 0x{keyboard_id:08X}ul') lines.append('') From ec9a78cc4a7ad202a16d83b8673fc77b87eb327c Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Thu, 16 Sep 2021 11:43:19 +1000 Subject: [PATCH 004/203] Doco update. --- data/xap/xap_0.1.0.hjson | 2 +- docs/xap_0.1.0.md | 2 +- docs/xap_0.2.0.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/data/xap/xap_0.1.0.hjson b/data/xap/xap_0.1.0.hjson index e24978f459ab..f74f17733990 100755 --- a/data/xap/xap_0.1.0.hjson +++ b/data/xap/xap_0.1.0.hjson @@ -59,7 +59,7 @@ ''' "struct{}": ''' - A structure of data, packing different objects together. Data is "compacted" -- there are no padding bytes between fields. Equivalent to a C-style `struct`. The order in which they're defined matches the order of the data in the response packet. + 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. ''' } diff --git a/docs/xap_0.1.0.md b/docs/xap_0.1.0.md index 8dac167209fd..8d53f276bf65 100644 --- a/docs/xap_0.1.0.md +++ b/docs/xap_0.1.0.md @@ -8,7 +8,7 @@ This document describes the requirements of the QMK XAP ("extensible application | Name | Definition | | -- | -- | -| _struct{}_ | A structure of data, packing different objects together. Data is "compacted" -- there are no padding bytes between fields. Equivalent to a C-style `struct`. The order in which they're defined matches the order of the data in the response packet. | +| _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_. | diff --git a/docs/xap_0.2.0.md b/docs/xap_0.2.0.md index 8dac167209fd..8d53f276bf65 100644 --- a/docs/xap_0.2.0.md +++ b/docs/xap_0.2.0.md @@ -8,7 +8,7 @@ This document describes the requirements of the QMK XAP ("extensible application | Name | Definition | | -- | -- | -| _struct{}_ | A structure of data, packing different objects together. Data is "compacted" -- there are no padding bytes between fields. Equivalent to a C-style `struct`. The order in which they're defined matches the order of the data in the response packet. | +| _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_. | From 1e723e66475324c800744739bf19153d561ddd67 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Mon, 14 Feb 2022 09:46:17 +1100 Subject: [PATCH 005/203] Output logging. --- builddefs/build_keyboard.mk | 44 ++++++++++++------------------------ builddefs/common_features.mk | 4 ++++ builddefs/message.mk | 1 + builddefs/xap.mk | 28 +++++++++++++++++++++++ 4 files changed, 48 insertions(+), 29 deletions(-) create mode 100644 builddefs/xap.mk diff --git a/builddefs/build_keyboard.mk b/builddefs/build_keyboard.mk index 3e9e4bcb7866..44acd964cc7a 100644 --- a/builddefs/build_keyboard.mk +++ b/builddefs/build_keyboard.mk @@ -159,10 +159,14 @@ ifneq ("$(wildcard $(KEYMAP_JSON))", "") # Add rules to generate the keymap files - indentation here is important $(KEYMAP_OUTPUT)/src/keymap.c: $(KEYMAP_JSON) - $(QMK_BIN) json2c --quiet --output $(KEYMAP_C) $(KEYMAP_JSON) + @$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD) + $(eval CMD=$(QMK_BIN) json2c --quiet --output $(KEYMAP_C) $(KEYMAP_JSON)) + @$(BUILD_CMD) $(KEYMAP_OUTPUT)/src/config.h: $(KEYMAP_JSON) - $(QMK_BIN) generate-config-h --quiet --keyboard $(KEYBOARD) --keymap $(KEYMAP) --output $(KEYMAP_H) + @$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD) + $(eval CMD=$(QMK_BIN) generate-config-h --quiet --keyboard $(KEYBOARD) --keymap $(KEYMAP) --output $(KEYMAP_H)) + @$(BUILD_CMD) generated-files: $(KEYMAP_OUTPUT)/src/config.h $(KEYMAP_OUTPUT)/src/keymap.c @@ -326,13 +330,19 @@ endif CONFIG_H += $(KEYBOARD_OUTPUT)/src/info_config.h $(KEYBOARD_OUTPUT)/src/layouts.h $(KEYBOARD_OUTPUT)/src/info_config.h: $(INFO_JSON_FILES) - $(QMK_BIN) generate-config-h --quiet --keyboard $(KEYBOARD) --output $(KEYBOARD_OUTPUT)/src/info_config.h + @$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD) + $(eval CMD=$(QMK_BIN) generate-config-h --quiet --keyboard $(KEYBOARD) --output $(KEYBOARD_OUTPUT)/src/info_config.h) + @$(BUILD_CMD) $(KEYBOARD_OUTPUT)/src/default_keyboard.h: $(INFO_JSON_FILES) - $(QMK_BIN) generate-keyboard-h --quiet --keyboard $(KEYBOARD) --output $(KEYBOARD_OUTPUT)/src/default_keyboard.h + @$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD) + $(eval CMD=$(QMK_BIN) generate-keyboard-h --quiet --keyboard $(KEYBOARD) --output $(KEYBOARD_OUTPUT)/src/default_keyboard.h) + @$(BUILD_CMD) $(KEYBOARD_OUTPUT)/src/layouts.h: $(INFO_JSON_FILES) - $(QMK_BIN) generate-layouts --quiet --keyboard $(KEYBOARD) --output $(KEYBOARD_OUTPUT)/src/layouts.h + @$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD) + $(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.h $(KEYBOARD_OUTPUT)/src/layouts.h @@ -399,30 +409,6 @@ VPATH += $(KEYBOARD_OUTPUT)/src VPATH += $(KEYMAP_OUTPUT)/src include $(BUILDDEFS_PATH)/common_features.mk - -# XAP embedded info.json -ifeq ($(strip $(XAP_ENABLE)), yes) - -$(KEYMAP_OUTPUT)/src/info_json_gz.h: $(INFO_JSON_FILES) - mkdir -p $(KEYMAP_OUTPUT)/src - $(QMK_BIN) info -f json -kb $(KEYBOARD) -km $(KEYMAP) | gzip -c9 > $(KEYMAP_OUTPUT)/src/info.json.gz - cd $(KEYMAP_OUTPUT)/src >/dev/null 2>&1 \ - && xxd -i info.json.gz info_json_gz.h \ - && cd - >/dev/null 2>&1 - -XAP_FILES := $(shell ls -1 data/xap/* | sort | xargs echo) - -$(KEYMAP_OUTPUT)/src/xap_generated.inl: $(XAP_FILES) - $(QMK_BIN) xap-generate-qmk-inc -o "$(KEYMAP_OUTPUT)/src/xap_generated.inl" - -$(KEYMAP_OUTPUT)/src/xap_generated.h: $(XAP_FILES) - $(QMK_BIN) xap-generate-qmk-h -o "$(KEYMAP_OUTPUT)/src/xap_generated.h" -kb $(KEYBOARD) - -generated-files: $(KEYMAP_OUTPUT)/src/info_json_gz.h $(KEYMAP_OUTPUT)/src/xap_generated.inl $(KEYMAP_OUTPUT)/src/xap_generated.h - -VPATH += $(KEYMAP_OUTPUT)/src -endif - include $(BUILDDEFS_PATH)/generic_features.mk include $(TMK_PATH)/protocol.mk include $(PLATFORM_PATH)/common.mk diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk index cbff9fa4fa58..63a5d57ec274 100644 --- a/builddefs/common_features.mk +++ b/builddefs/common_features.mk @@ -45,6 +45,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) diff --git a/builddefs/message.mk b/builddefs/message.mk index d441f560be7b..07d0e07ce80a 100644 --- a/builddefs/message.mk +++ b/builddefs/message.mk @@ -63,6 +63,7 @@ MSG_COMPILING_CXX = Compiling: MSG_ASSEMBLING = Assembling: MSG_CLEANING = Cleaning project: MSG_CREATING_LIBRARY = Creating library: +MSG_GENERATING = Generating: MSG_SUBMODULE_DIRTY = $(WARN_COLOR)WARNING:$(NO_COLOR) Some git submodules are out of date or modified.\n\ Please consider running $(BOLD)make git-submodule$(NO_COLOR).\n\n MSG_NO_CMP = $(ERROR_COLOR)Error:$(NO_COLOR)$(BOLD) cmp command not found, please install diffutils\n$(NO_COLOR) diff --git a/builddefs/xap.mk b/builddefs/xap.mk new file mode 100644 index 000000000000..c2ccf1454180 --- /dev/null +++ b/builddefs/xap.mk @@ -0,0 +1,28 @@ +# Copyright 2022 Nick Brassel (@tzarc) +# SPDX-License-Identifier: GPL-2.0-or-later + +# XAP embedded info.json +$(KEYMAP_OUTPUT)/src/info_json_gz.h: $(INFO_JSON_FILES) + @$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD) + mkdir -p $(KEYMAP_OUTPUT)/src + $(eval CMD=$(QMK_BIN) info -f json -kb $(KEYBOARD) -km $(KEYMAP) | gzip -c9 > $(KEYMAP_OUTPUT)/src/info.json.gz \ + && cd $(KEYMAP_OUTPUT)/src >/dev/null 2>&1 \ + && xxd -i info.json.gz info_json_gz.h \ + && cd - >/dev/null 2>&1) + @$(BUILD_CMD) + +XAP_FILES := $(shell ls -1 data/xap/* | sort | xargs echo) + +$(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") + @$(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)) + @$(BUILD_CMD) + +generated-files: $(KEYMAP_OUTPUT)/src/info_json_gz.h $(KEYMAP_OUTPUT)/src/xap_generated.inl $(KEYMAP_OUTPUT)/src/xap_generated.h + +VPATH += $(KEYMAP_OUTPUT)/src From 69e9c80ec3e1af38a8a88b44737a138b39c45d21 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Tue, 15 Feb 2022 05:19:13 +1100 Subject: [PATCH 006/203] Reworked docs rendering using jinja2. --- data/templates/xap/docs/docs.md.j2 | 7 ++ data/templates/xap/docs/response_flags.md.j2 | 9 +++ .../templates/xap/docs/term_definitions.md.j2 | 5 ++ data/templates/xap/docs/type_docs.md.j2 | 5 ++ data/xap/xap_0.0.1.hjson | 6 +- docs/xap_0.0.1.md | 7 +- docs/xap_0.1.0.md | 13 +-- docs/xap_0.2.0.md | 13 +-- lib/python/qmk/cli/__init__.py | 3 +- lib/python/qmk/xap/common.py | 15 +++- lib/python/qmk/xap/gen_docs/generator.py | 80 ++----------------- requirements.txt | 1 + 12 files changed, 70 insertions(+), 94 deletions(-) create mode 100644 data/templates/xap/docs/docs.md.j2 create mode 100644 data/templates/xap/docs/response_flags.md.j2 create mode 100644 data/templates/xap/docs/term_definitions.md.j2 create mode 100644 data/templates/xap/docs/type_docs.md.j2 diff --git a/data/templates/xap/docs/docs.md.j2 b/data/templates/xap/docs/docs.md.j2 new file mode 100644 index 000000000000..7d6225ff7b08 --- /dev/null +++ b/data/templates/xap/docs/docs.md.j2 @@ -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 %} \ No newline at end of file diff --git a/data/templates/xap/docs/response_flags.md.j2 b/data/templates/xap/docs/response_flags.md.j2 new file mode 100644 index 000000000000..852db16fd93c --- /dev/null +++ b/data/templates/xap/docs/response_flags.md.j2 @@ -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 %} \ No newline at end of file diff --git a/data/templates/xap/docs/term_definitions.md.j2 b/data/templates/xap/docs/term_definitions.md.j2 new file mode 100644 index 000000000000..8afdb9c3e215 --- /dev/null +++ b/data/templates/xap/docs/term_definitions.md.j2 @@ -0,0 +1,5 @@ +| Name | Definition | +| -- | -- | +{%- for type, definition in xap.term_definitions | dictsort %} +| _{{ type }}_ | {{ definition }} | +{%- endfor %} \ No newline at end of file diff --git a/data/templates/xap/docs/type_docs.md.j2 b/data/templates/xap/docs/type_docs.md.j2 new file mode 100644 index 000000000000..787d94285323 --- /dev/null +++ b/data/templates/xap/docs/type_docs.md.j2 @@ -0,0 +1,5 @@ +| Name | Definition | +| -- | -- | +{%- for type, definition in xap.type_docs | dictsort %} +| _{{ type }}_ | {{ definition }} | +{%- endfor %} \ No newline at end of file diff --git a/data/xap/xap_0.0.1.hjson b/data/xap/xap_0.0.1.hjson index 34fd21d62370..9b19526244da 100755 --- a/data/xap/xap_0.0.1.hjson +++ b/data/xap/xap_0.0.1.hjson @@ -9,13 +9,13 @@ order: [ page_header type_docs - !type_docs! + !type_docs.md.j2 term_definitions - !term_definitions! + !term_definitions.md.j2 request_response reserved_tokens response_flags - !response_flags! + !response_flags.md.j2 example_conversation ] diff --git a/docs/xap_0.0.1.md b/docs/xap_0.0.1.md index ff45f1ae4500..336176c33c14 100644 --- a/docs/xap_0.0.1.md +++ b/docs/xap_0.0.1.md @@ -41,10 +41,11 @@ 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 | +| -- | -- | -- | -- | -- | -- | -- | -- | +| `-` | `-` | `-` | `-` | `-` | `-` | `-` | `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). -* `Bit 0`: 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": diff --git a/docs/xap_0.1.0.md b/docs/xap_0.1.0.md index 8d53f276bf65..f3911d7220d5 100644 --- a/docs/xap_0.1.0.md +++ b/docs/xap_0.1.0.md @@ -50,13 +50,14 @@ Any request will generate at least one corresponding response, with the exceptio 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 | Unlocking | - | - | - | - | Secure Failure | Success | +| -- | -- | -- | -- | -- | -- | -- | -- | +| `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). -* `Bit 7`: When this bit is set, an _unlock sequence_ has completed, and _secure routes_ may be invoked. -* `Bit 6`: When this bit is set, an _unlock sequence_ is in progress. -* `Bit 1`: When this bit is set, the requested _route_ was marked _secure_ but an _unlock sequence_ has not completed. -* `Bit 0`: 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": diff --git a/docs/xap_0.2.0.md b/docs/xap_0.2.0.md index 8d53f276bf65..f3911d7220d5 100644 --- a/docs/xap_0.2.0.md +++ b/docs/xap_0.2.0.md @@ -50,13 +50,14 @@ Any request will generate at least one corresponding response, with the exceptio 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 | Unlocking | - | - | - | - | Secure Failure | Success | +| -- | -- | -- | -- | -- | -- | -- | -- | +| `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). -* `Bit 7`: When this bit is set, an _unlock sequence_ has completed, and _secure routes_ may be invoked. -* `Bit 6`: When this bit is set, an _unlock sequence_ is in progress. -* `Bit 1`: When this bit is set, the requested _route_ was marked _secure_ but an _unlock sequence_ has not completed. -* `Bit 0`: 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": diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py index 5bc809d503f4..3197d9d7f223 100644 --- a/lib/python/qmk/cli/__init__.py +++ b/lib/python/qmk/cli/__init__.py @@ -16,7 +16,8 @@ # A mapping of package name to importable name 'pep8-naming': 'pep8ext_naming', 'pyusb': 'usb.core', - 'qmk-dotty-dict': 'dotty_dict' + 'qmk-dotty-dict': 'dotty_dict', + 'Jinja2': 'jinja2' } safe_commands = [ diff --git a/lib/python/qmk/xap/common.py b/lib/python/qmk/xap/common.py index bec82d727bb9..d1e0d5dbb034 100755 --- a/lib/python/qmk/xap/common.py +++ b/lib/python/qmk/xap/common.py @@ -1,10 +1,21 @@ """This script handles the XAP protocol data files. """ -import re +import os import hjson +from jinja2 import Environment, FileSystemLoader, select_autoescape +from qmk.constants import QMK_FIRMWARE from typing import OrderedDict -from qmk.constants import QMK_FIRMWARE + +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 _merge_ordered_dicts(dicts): diff --git a/lib/python/qmk/xap/gen_docs/generator.py b/lib/python/qmk/xap/gen_docs/generator.py index 3e0ce5289624..bcb90351e99c 100755 --- a/lib/python/qmk/xap/gen_docs/generator.py +++ b/lib/python/qmk/xap/gen_docs/generator.py @@ -2,64 +2,7 @@ """ import hjson from qmk.constants import QMK_FIRMWARE -from qmk.xap.common import get_xap_definition_files, update_xap_definitions - - -def _update_type_docs(overall): - defs = overall['type_docs'] - - type_docs = [] - for (k, v) in sorted(defs.items(), key=lambda x: x[0]): - type_docs.append(f'| _{k}_ | {v} |') - - desc_str = "\n".join(type_docs) - - overall['documentation']['!type_docs!'] = f'''\ -| Name | Definition | -| -- | -- | -{desc_str} -''' - - -def _update_term_definitions(overall): - defs = overall['term_definitions'] - - term_descriptions = [] - for (k, v) in sorted(defs.items(), key=lambda x: x[0]): - term_descriptions.append(f'| _{k}_ | {v} |') - - desc_str = "\n".join(term_descriptions) - - overall['documentation']['!term_definitions!'] = f'''\ -| Name | Definition | -| -- | -- | -{desc_str} -''' - - -def _update_response_flags(overall): - flags = overall['response_flags']['bits'] - for n in range(0, 8): - if str(n) not in flags: - flags[str(n)] = {"name": "-", "description": "-"} - - header = '| ' + " | ".join([f'Bit {n}' for n in range(7, -1, -1)]) + ' |' - dividers = '|' + "|".join(['--' for n in range(7, -1, -1)]) + '|' - bit_names = '| ' + " | ".join([flags[str(n)]['name'] for n in range(7, -1, -1)]) + ' |' - - bit_descriptions = '' - for n in range(7, -1, -1): - bit_desc = flags[str(n)] - if bit_desc['name'] != '-': - desc = bit_desc['description'] - bit_descriptions = bit_descriptions + f'\n* `Bit {n}`: {desc}' - - overall['documentation']['!response_flags!'] = f'''\ -{header} -{dividers} -{bit_names} -{bit_descriptions} -''' +from qmk.xap.common import get_xap_definition_files, update_xap_definitions, render_xap_output def generate_docs(): @@ -69,27 +12,18 @@ def generate_docs(): overall = None for file in get_xap_definition_files(): - overall = update_xap_definitions(overall, hjson.load(file.open(encoding='utf-8'))) - try: - if 'type_docs' in overall: - _update_type_docs(overall) - if 'term_definitions' in overall: - _update_term_definitions(overall) - if 'response_flags' in overall: - _update_response_flags(overall) - except: - print(hjson.dumps(overall)) - exit(1) + # 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: - for e in overall['documentation']['order']: - out_file.write(overall['documentation'][e].strip()) - out_file.write('\n\n') + out_file.write(output) output_doc = QMK_FIRMWARE / "docs" / f"xap_protocol.md" with open(output_doc, "w", encoding='utf-8') as out_file: diff --git a/requirements.txt b/requirements.txt index 6357b4e2eb0d..7cf2c0b1287c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,6 +5,7 @@ colorama fnvhash hid hjson +Jinja2 jsonschema>=3 milc>=1.4.2 pygments From 6c7afbb859a9b3557268802ac2fcea2450260b5c Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Wed, 16 Feb 2022 10:53:35 +1100 Subject: [PATCH 007/203] Migrate XAP docs generator into CLI now that most logic is in Jinja2 files. --- lib/python/qmk/cli/xap/generate_docs.py | 32 ++++++++++++++++++-- lib/python/qmk/xap/gen_docs/__init__.py | 0 lib/python/qmk/xap/gen_docs/generator.py | 37 ------------------------ 3 files changed, 30 insertions(+), 39 deletions(-) delete mode 100644 lib/python/qmk/xap/gen_docs/__init__.py delete mode 100755 lib/python/qmk/xap/gen_docs/generator.py diff --git a/lib/python/qmk/cli/xap/generate_docs.py b/lib/python/qmk/cli/xap/generate_docs.py index bf2fdb86bbb9..d77a418bf7e1 100755 --- a/lib/python/qmk/cli/xap/generate_docs.py +++ b/lib/python/qmk/cli/xap/generate_docs.py @@ -1,11 +1,39 @@ """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 -from qmk.xap.gen_docs.generator import generate_docs @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/`. """ - generate_docs() \ No newline at end of file + 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" / f"xap_protocol.md" + with open(output_doc, "w", encoding='utf-8') as out_file: + out_file.write('''\ +# XAP Protocol Reference + +''') + + for file in reversed(sorted(docs_list)): + ver = file.stem[4:] + out_file.write(f'* [XAP Version {ver}]({file.name})\n') diff --git a/lib/python/qmk/xap/gen_docs/__init__.py b/lib/python/qmk/xap/gen_docs/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/lib/python/qmk/xap/gen_docs/generator.py b/lib/python/qmk/xap/gen_docs/generator.py deleted file mode 100755 index bcb90351e99c..000000000000 --- a/lib/python/qmk/xap/gen_docs/generator.py +++ /dev/null @@ -1,37 +0,0 @@ -"""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 - - -def generate_docs(): - """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" / f"xap_protocol.md" - with open(output_doc, "w", encoding='utf-8') as out_file: - out_file.write('''\ -# XAP Protocol Reference - -''') - - for file in reversed(sorted(docs_list)): - ver = file.stem[4:] - out_file.write(f'* [XAP Version {ver}]({file.name})\n') From c3ac89d1c97640311350203b0d1b85557daca8b1 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Wed, 9 Mar 2022 20:01:20 +1100 Subject: [PATCH 008/203] `qmk format-c`, `qmk format-python` --- lib/python/qmk/xap/gen_firmware/inline_generator.py | 2 -- quantum/xap/xap.c | 6 ++++-- quantum/xap/xap_handlers.c | 8 ++++++-- tmk_core/protocol/lufa/lufa.c | 6 +++--- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/python/qmk/xap/gen_firmware/inline_generator.py b/lib/python/qmk/xap/gen_firmware/inline_generator.py index 756b0e866642..5d8f10a12b05 100755 --- a/lib/python/qmk/xap/gen_firmware/inline_generator.py +++ b/lib/python/qmk/xap/gen_firmware/inline_generator.py @@ -1,7 +1,5 @@ """This script generates the XAP protocol generated header to be compiled into QMK. """ -import pyhash - from qmk.casing import to_snake from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE from qmk.xap.common import latest_xap_defs, route_conditions diff --git a/quantum/xap/xap.c b/quantum/xap/xap.c index df25f799d596..185213c8339e 100644 --- a/quantum/xap/xap.c +++ b/quantum/xap/xap.c @@ -22,7 +22,7 @@ #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_UNUSED = 0, // "Unused" needs to be zero -- undefined routes (through preprocessor) will be skipped XAP_ROUTE, XAP_EXECUTE, XAP_VALUE, @@ -113,4 +113,6 @@ void xap_execute_route(xap_token_t token, const xap_route_t *routes, size_t max_ 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); } +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); +} diff --git a/quantum/xap/xap_handlers.c b/quantum/xap/xap_handlers.c index 7c3d8127ce91..492d3d25bc02 100644 --- a/quantum/xap/xap_handlers.c +++ b/quantum/xap/xap_handlers.c @@ -18,7 +18,9 @@ #include #include -void xap_respond_failure(xap_token_t token, xap_response_flags_t response_flags) { xap_send(token, response_flags, 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); @@ -36,4 +38,6 @@ bool xap_respond_u32(xap_token_t token, uint32_t value) { return true; } -uint32_t xap_route_qmk_ffffffffffffffff_getter(void) { return 0x12345678; } +uint32_t xap_route_qmk_ffffffffffffffff_getter(void) { + return 0x12345678; +} diff --git a/tmk_core/protocol/lufa/lufa.c b/tmk_core/protocol/lufa/lufa.c index 47c3d30c3ca4..0dcf1036c48a 100644 --- a/tmk_core/protocol/lufa/lufa.c +++ b/tmk_core/protocol/lufa/lufa.c @@ -293,7 +293,7 @@ static void xap_task(void) { } } } -#endif // XAP_ENABLE +#endif // XAP_ENABLE /******************************************************************************* * Console @@ -561,7 +561,7 @@ void EVENT_USB_Device_ConfigurationChanged(void) { /* 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 +#endif // XAP_ENABLE #ifdef CONSOLE_ENABLE /* Setup console endpoint */ @@ -1189,7 +1189,7 @@ void protocol_post_task(void) { #endif #ifdef XAP_ENABLE - xap_task(); + xap_task(); #endif #if !defined(INTERRUPT_CONTROL_ENDPOINT) From 2b4724bd83c13c0abfe867ae69334a756babeaa9 Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 15 Mar 2022 17:59:12 +0000 Subject: [PATCH 009/203] Convert info_json_gz.h generation to CLI --- builddefs/xap.mk | 6 +-- lib/python/qmk/cli/format/text.py | 10 +---- lib/python/qmk/cli/xap/generate_qmk.py | 57 +++++++++++++++++++++++++- lib/python/qmk/commands.py | 23 +++++++++++ 4 files changed, 82 insertions(+), 14 deletions(-) diff --git a/builddefs/xap.mk b/builddefs/xap.mk index c2ccf1454180..e599c8122113 100644 --- a/builddefs/xap.mk +++ b/builddefs/xap.mk @@ -4,11 +4,7 @@ # XAP embedded info.json $(KEYMAP_OUTPUT)/src/info_json_gz.h: $(INFO_JSON_FILES) @$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD) - mkdir -p $(KEYMAP_OUTPUT)/src - $(eval CMD=$(QMK_BIN) info -f json -kb $(KEYBOARD) -km $(KEYMAP) | gzip -c9 > $(KEYMAP_OUTPUT)/src/info.json.gz \ - && cd $(KEYMAP_OUTPUT)/src >/dev/null 2>&1 \ - && xxd -i info.json.gz info_json_gz.h \ - && cd - >/dev/null 2>&1) + $(eval CMD=$(QMK_BIN) xap-generate-info-h -o "$(KEYMAP_OUTPUT)/src/info_json_gz.h" -kb $(KEYBOARD) -km $(KEYMAP)) @$(BUILD_CMD) XAP_FILES := $(shell ls -1 data/xap/* | sort | xargs echo) diff --git a/lib/python/qmk/cli/format/text.py b/lib/python/qmk/cli/format/text.py index 6dd451189652..fa86b4379a16 100644 --- a/lib/python/qmk/cli/format/text.py +++ b/lib/python/qmk/cli/format/text.py @@ -6,19 +6,13 @@ 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: diff --git a/lib/python/qmk/cli/xap/generate_qmk.py b/lib/python/qmk/cli/xap/generate_qmk.py index 0cb2fec1954a..ff43e2863d3b 100755 --- a/lib/python/qmk/cli/xap/generate_qmk.py +++ b/lib/python/qmk/cli/xap/generate_qmk.py @@ -1,11 +1,19 @@ """This script generates the XAP protocol generated sources to be compiled into QMK firmware. """ +import json +import gzip + from milc import cli from qmk.path import normpath +from qmk.info import info_json +from qmk.commands import get_chunks, dump_lines +from qmk.keyboard import keyboard_completer, keyboard_folder from qmk.xap.gen_firmware.inline_generator import generate_inline from qmk.xap.gen_firmware.header_generator import generate_header +from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE + @cli.argument('-o', '--output', type=normpath, help='File to write to') @cli.subcommand('Generates the XAP protocol include.', hidden=False if cli.config.user.developer else True) @@ -16,9 +24,56 @@ def xap_generate_qmk_inc(cli): @cli.argument('-o', '--output', type=normpath, help='File to write to') -@cli.argument('-kb', '--keyboard', help='Name of the keyboard') +@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Name of the keyboard') @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 + if not cli.args.keyboard: + cli.log.error('Missing parameter: --keyboard') + cli.subcommands['xap-generate-qmk-h'].print_help() + return False generate_header(cli.args.output, cli.args.keyboard) + + +@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 info.json payload include.', hidden=False if cli.config.user.developer else True) +def xap_generate_info_h(cli): + """Generates the XAP info.json payload header file, generated during normal build. + """ + # Determine our keyboard + if not cli.args.keyboard: + cli.log.error('Missing parameter: --keyboard') + cli.subcommands['xap-generate-info-h'].print_help() + return False + + # TODO: merge in keymap level content + # Build the info.json file + kb_info_json = info_json(cli.args.keyboard) + + # Minify + str_data = json.dumps(kb_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('unsigned char info_json_gz[] = {') + lines.append(data) + lines.append('};') + lines.append(f'unsigned int info_json_gz_len = {data_len};') + + dump_lines(cli.args.output, lines) diff --git a/lib/python/qmk/commands.py b/lib/python/qmk/commands.py index 5216bcdba52f..56b2ccc50cf4 100644 --- a/lib/python/qmk/commands.py +++ b/lib/python/qmk/commands.py @@ -6,6 +6,7 @@ from pathlib import Path from subprocess import DEVNULL from time import strftime +from itertools import islice from milc import cli import jsonschema @@ -360,3 +361,25 @@ def in_virtualenv(): """ active_prefix = getattr(sys, "base_prefix", None) or getattr(sys, "real_prefix", None) or sys.prefix 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): + """Handle dumping to stdout or file + Creates parent folders if required + """ + generated = '\n'.join(lines) + if output_file: + if output_file.name == '-': + print(generated) + else: + output_file.parent.mkdir(parents=True, exist_ok=True) + if output_file.exists(): + output_file.replace(output_file.parent / (output_file.name + '.bak')) + output_file.write_text(generated) From 7e8f0d49ea511385476746c977e7086e35fc4a55 Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 15 Mar 2022 23:57:41 +0000 Subject: [PATCH 010/203] Temp bodge for unit tests? --- .github/workflows/unit_test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/unit_test.yml b/.github/workflows/unit_test.yml index 26bcb2f51106..726ce19f0c00 100644 --- a/.github/workflows/unit_test.yml +++ b/.github/workflows/unit_test.yml @@ -26,5 +26,7 @@ jobs: - uses: actions/checkout@v2 with: submodules: recursive + - name: Install dependencies + run: pip3 install -r requirements-dev.txt - name: Run tests run: make test:all From 52d3b9dcc5b5342ae1a682da50406fcead2ee3cd Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 16 Mar 2022 00:10:30 +0000 Subject: [PATCH 011/203] fix up for pytest --- lib/python/qmk/cli/format/text.py | 1 - lib/python/qmk/cli/xap/generate_docs.py | 2 +- lib/python/qmk/xap/gen_firmware/inline_generator.py | 8 ++++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/python/qmk/cli/format/text.py b/lib/python/qmk/cli/format/text.py index fa86b4379a16..ff44cf2cd981 100644 --- a/lib/python/qmk/cli/format/text.py +++ b/lib/python/qmk/cli/format/text.py @@ -1,6 +1,5 @@ """Ensure text files have the proper line endings. """ -from itertools import islice from subprocess import DEVNULL from milc import cli diff --git a/lib/python/qmk/cli/xap/generate_docs.py b/lib/python/qmk/cli/xap/generate_docs.py index d77a418bf7e1..e399b6ddb122 100755 --- a/lib/python/qmk/cli/xap/generate_docs.py +++ b/lib/python/qmk/cli/xap/generate_docs.py @@ -27,7 +27,7 @@ def xap_generate_docs(cli): with open(output_doc, "w", encoding='utf-8') as out_file: out_file.write(output) - output_doc = QMK_FIRMWARE / "docs" / f"xap_protocol.md" + output_doc = QMK_FIRMWARE / "docs" / "xap_protocol.md" with open(output_doc, "w", encoding='utf-8') as out_file: out_file.write('''\ # XAP Protocol Reference diff --git a/lib/python/qmk/xap/gen_firmware/inline_generator.py b/lib/python/qmk/xap/gen_firmware/inline_generator.py index 5d8f10a12b05..357a4ab72260 100755 --- a/lib/python/qmk/xap/gen_firmware/inline_generator.py +++ b/lib/python/qmk/xap/gen_firmware/inline_generator.py @@ -66,7 +66,7 @@ def _append_routing_table_declaration(lines, container, container_id, route_stac for constant in container['return_constant']: lines.append(f' {constant},') - lines.append(f'}};') + lines.append('}};') elif container['return_type'] == 'string': constant = container['return_constant'] @@ -87,10 +87,10 @@ def _append_routing_table_declaration(lines, container, container_id, route_stac def _append_routing_table_entry_flags(lines, container, container_id, route_stack): is_secure = 1 if ('secure' in container and container['secure'] is True) else 0 - lines.append(f' .flags = {{') + lines.append(' .flags = {{') lines.append(f' .type = {_get_route_type(container)},') lines.append(f' .is_secure = {is_secure},') - lines.append(f' }},') + lines.append(' }},') def _append_routing_table_entry_route(lines, container, container_id, route_stack): @@ -145,7 +145,7 @@ def _append_routing_table_entry(lines, container, container_id, route_stack): if container['return_type'] == 'u32': _append_routing_table_entry_u32getter(lines, container, container_id, route_stack) - lines.append(f' }},') + lines.append(' }},') if condition: lines.append(f'#endif // {condition}') From 9fd4db1fc7f635afae796a126e99fcc29f86eddc Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 16 Mar 2022 10:58:21 +0000 Subject: [PATCH 012/203] fix up for pytest - remove fstring escaping --- lib/python/qmk/xap/gen_firmware/inline_generator.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/python/qmk/xap/gen_firmware/inline_generator.py b/lib/python/qmk/xap/gen_firmware/inline_generator.py index 357a4ab72260..a40e40ec984e 100755 --- a/lib/python/qmk/xap/gen_firmware/inline_generator.py +++ b/lib/python/qmk/xap/gen_firmware/inline_generator.py @@ -66,7 +66,7 @@ def _append_routing_table_declaration(lines, container, container_id, route_stac for constant in container['return_constant']: lines.append(f' {constant},') - lines.append('}};') + lines.append('};') elif container['return_type'] == 'string': constant = container['return_constant'] @@ -87,10 +87,10 @@ def _append_routing_table_declaration(lines, container, container_id, route_stac def _append_routing_table_entry_flags(lines, container, container_id, route_stack): is_secure = 1 if ('secure' in container and container['secure'] is True) else 0 - lines.append(' .flags = {{') + lines.append(' .flags = {') lines.append(f' .type = {_get_route_type(container)},') lines.append(f' .is_secure = {is_secure},') - lines.append(' }},') + lines.append(' },') def _append_routing_table_entry_route(lines, container, container_id, route_stack): @@ -145,7 +145,7 @@ def _append_routing_table_entry(lines, container, container_id, route_stack): if container['return_type'] == 'u32': _append_routing_table_entry_u32getter(lines, container, container_id, route_stack) - lines.append(' }},') + lines.append(' },') if condition: lines.append(f'#endif // {condition}') From 31c486470591409767d797dba6ffcfe456ca1f13 Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 16 Mar 2022 19:45:37 +0000 Subject: [PATCH 013/203] Crude CLI device discovery --- lib/python/qmk/cli/__init__.py | 1 + lib/python/qmk/cli/xap/__init__.py | 79 ++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py index 3197d9d7f223..f0e86dd1240b 100644 --- a/lib/python/qmk/cli/__init__.py +++ b/lib/python/qmk/cli/__init__.py @@ -70,6 +70,7 @@ 'qmk.cli.new.keymap', 'qmk.cli.pyformat', 'qmk.cli.pytest', + 'qmk.cli.xap', 'qmk.cli.xap.generate_docs', 'qmk.cli.xap.generate_json', 'qmk.cli.xap.generate_qmk', diff --git a/lib/python/qmk/cli/xap/__init__.py b/lib/python/qmk/cli/xap/__init__.py index e69de29bb2d1..963617a6eeeb 100755 --- a/lib/python/qmk/cli/xap/__init__.py +++ b/lib/python/qmk/cli/xap/__init__.py @@ -0,0 +1,79 @@ +"""Interactions with compatible XAP devices +""" +import json +import random + +from milc import cli + + +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(cli.args.device.lower()) + + +def _search(): + devices = filter(_is_xap_usage, hid.enumerate()) + if cli.args.device: + devices = filter(_is_filtered_device, devices) + + return list(devices) + + +def _list_devices(): + """Dump out available devices + """ + cli.log.info('Available devices:') + devices = _search() + for dev in devices: + device = hid.Device(path=dev['path']) + + data = _query_device(device) + cli.log.info(" %04x:%04x %s %s [API:%s]", dev['vendor_id'], dev['product_id'], dev['manufacturer_string'], dev['product_string'], data['ver']) + + if cli.config.general.verbose: + # TODO: better formatting like "lsusb -v" + cli.log.info(" " + json.dumps(data)) + + +def _query_device(device): + # gen token + tok = random.getrandbits(16) + temp = tok.to_bytes(2, byteorder='big') + + # send with padding + padding = b"\x00" * 59 + device.write(temp + b'\x02\x00\x00' + padding) + + # get resp + array_alpha = device.read(8, 100) + # hex_string = " ".join("%02x" % b for b in array_alpha) + + # validate tok sent == resp + ver = "UNKNOWN" + if str(temp) == str(array_alpha[:2]): + # to BCD string + a = (array_alpha[7] << 24) + (array_alpha[6] << 16) + (array_alpha[5] << 8) + (array_alpha[4]) + ver = f'{a>>24}.{a>>16 & 0xFF}.{a & 0xFFFF}' + + return {'ver': ver} + + +@cli.argument('-d', '--device', help='device to select - uses format :.') +@cli.argument('-i', '--index', default=0, help='device index to select.') +@cli.argument('-l', '--list', arg_only=True, action='store_true', help='List available devices.') +@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 + """ + # Lazy load to avoid issues + global hid + import hid + + if cli.args.list: + return _list_devices() + + cli.log.warn("TODO: Device specific stuff") From 72602a344330b50cd746ca8d8b96eafd76fbf473 Mon Sep 17 00:00:00 2001 From: zvecr Date: Fri, 18 Mar 2022 01:53:39 +0000 Subject: [PATCH 014/203] Fixup after merge --- lib/python/qmk/commands.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/python/qmk/commands.py b/lib/python/qmk/commands.py index eec347d2604b..5def7906447e 100644 --- a/lib/python/qmk/commands.py +++ b/lib/python/qmk/commands.py @@ -4,6 +4,8 @@ import sys import shutil from pathlib import Path +from time import strftime +from itertools import islice from subprocess import DEVNULL from milc import cli @@ -96,7 +98,7 @@ def get_git_version(current_time=None, repo_dir='.', check_dir='.'): git_describe_cmd = ['git', 'describe', '--abbrev=6', '--dirty', '--always', '--tags'] if current_time is None: - current_time = strftime(time_fmt) + current_time = strftime('%Y-%m-%d-%H:%M:%S') if repo_dir != '.': repo_dir = Path('lib') / repo_dir From 5bb6173cc7c0a56c9baac401385e5ac14565738f Mon Sep 17 00:00:00 2001 From: zvecr Date: Fri, 18 Mar 2022 17:01:02 +0000 Subject: [PATCH 015/203] Fixup after merge --- lib/python/qmk/xap/gen_firmware/header_generator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/python/qmk/xap/gen_firmware/header_generator.py b/lib/python/qmk/xap/gen_firmware/header_generator.py index 24dc8d34508e..ea299b18f568 100755 --- a/lib/python/qmk/xap/gen_firmware/header_generator.py +++ b/lib/python/qmk/xap/gen_firmware/header_generator.py @@ -3,7 +3,7 @@ import re from fnvhash import fnv1a_32 -from qmk.commands import get_git_version +from qmk.git import git_get_version from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE from qmk.xap.common import latest_xap_defs, route_conditions @@ -105,7 +105,7 @@ def generate_header(output_file, keyboard): 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.match(get_git_version()) + b = prog.match(git_get_version() or "") lines.append(f'#define QMK_BCD_VERSION 0x{int(b.group(1)):02d}{int(b.group(2)):02d}{int(b.group(3)):04d}ul') keyboard_id = fnv1a_32(bytes(keyboard, 'utf-8')) lines.append(f'#define XAP_KEYBOARD_IDENTIFIER 0x{keyboard_id:08X}ul') From 4d4b013e5bef5b011b2edd4c77958b50d355cd62 Mon Sep 17 00:00:00 2001 From: zvecr Date: Fri, 18 Mar 2022 17:03:54 +0000 Subject: [PATCH 016/203] Fixup after merge --- lib/python/qmk/commands.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/python/qmk/commands.py b/lib/python/qmk/commands.py index 7021e01b325e..db1a12c4d93f 100644 --- a/lib/python/qmk/commands.py +++ b/lib/python/qmk/commands.py @@ -3,6 +3,7 @@ import os import sys import shutil +from itertools import islice from pathlib import Path from milc import cli From aaf4fcbe5a69529d38d95121a072ce508425f596 Mon Sep 17 00:00:00 2001 From: zvecr Date: Fri, 18 Mar 2022 19:51:04 +0000 Subject: [PATCH 017/203] Merge chibios support --- tmk_core/protocol/chibios/chibios.c | 7 ++++ tmk_core/protocol/chibios/usb_main.c | 61 ++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/tmk_core/protocol/chibios/chibios.c b/tmk_core/protocol/chibios/chibios.c index c9a480c32555..4f4ad2b655c0 100644 --- a/tmk_core/protocol/chibios/chibios.c +++ b/tmk_core/protocol/chibios/chibios.c @@ -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 } diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index d9aa351ecbe2..a95ca79da954 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -53,6 +53,10 @@ extern keymap_config_t keymap_config; # include "joystick.h" #endif +#ifdef XAP_ENABLE +# include "xap.h" +#endif + /* --------------------------------------------------------- * Global interface variables and declarations * --------------------------------------------------------- @@ -313,6 +317,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 @@ -346,6 +353,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 @@ -1111,6 +1126,52 @@ 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, uint8_t response_flags, const void *data, size_t length) { + uint8_t rdata[XAP_EPSIZE] = {0}; + *(xap_token_t *)&rdata[0] = token; + if (length > (XAP_EPSIZE - 4)) response_flags &= ~(XAP_RESPONSE_FLAG_SUCCESS); + rdata[2] = response_flags; + if (response_flags & (XAP_RESPONSE_FLAG_SUCCESS)) { + rdata[3] = (uint8_t)length; + if (data != NULL) { + memcpy(&rdata[4], data, length); + } + } + xap_send_base(rdata, sizeof(rdata)); +} + +void xap_receive_base(const void *data) { + const uint8_t *u8data = (const uint8_t *)data; + xap_token_t token = *(xap_token_t *)&u8data[0]; + uint8_t length = u8data[2]; + if (length <= (XAP_EPSIZE - 3)) { + xap_receive(token, &u8data[3], 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) { From c27edf4e64241d7823c5427d480b3962c3d87905 Mon Sep 17 00:00:00 2001 From: zvecr Date: Fri, 18 Mar 2022 20:23:28 +0000 Subject: [PATCH 018/203] vusb prep --- quantum/xap/xap.c | 1 - tmk_core/protocol/report.h | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/quantum/xap/xap.c b/quantum/xap/xap.c index 185213c8339e..b348d366fb76 100644 --- a/quantum/xap/xap.c +++ b/quantum/xap/xap.c @@ -16,7 +16,6 @@ #include #include -#include #define QSTR2(z) #z #define QSTR(z) QSTR2(z) diff --git a/tmk_core/protocol/report.h b/tmk_core/protocol/report.h index e71e63d81ce6..7bbeb78af797 100644 --- a/tmk_core/protocol/report.h +++ b/tmk_core/protocol/report.h @@ -32,8 +32,7 @@ enum hid_report_ids { REPORT_ID_PROGRAMMABLE_BUTTON, REPORT_ID_NKRO, REPORT_ID_JOYSTICK, - REPORT_ID_DIGITIZER, - REPORT_ID_XAP + REPORT_ID_DIGITIZER }; /* Mouse buttons */ From a5204887a8533987640b61d82e5f597dd9145e76 Mon Sep 17 00:00:00 2001 From: zvecr Date: Fri, 18 Mar 2022 21:37:23 +0000 Subject: [PATCH 019/203] maybe vusb --- tmk_core/protocol/vusb/protocol.c | 14 ++- tmk_core/protocol/vusb/vusb.c | 178 +++++++++++++++++++++++++++++- tmk_core/protocol/vusb/vusb.h | 7 ++ 3 files changed, 197 insertions(+), 2 deletions(-) diff --git a/tmk_core/protocol/vusb/protocol.c b/tmk_core/protocol/vusb/protocol.c index 20d053048364..596735f40074 100644 --- a/tmk_core/protocol/vusb/protocol.c +++ b/tmk_core/protocol/vusb/protocol.c @@ -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; @@ -158,11 +162,19 @@ void protocol_task(void) { #ifdef RAW_ENABLE usbPoll(); - if (usbConfiguration && usbInterruptIsReady3()) { + if (usbConfiguration && usbInterruptIsReady4()) { raw_hid_task(); } #endif +#ifdef XAP_ENABLE + usbPoll(); + + if (usbConfiguration && usbInterruptIsReady4()) { + xap_task(); + } +#endif + #ifdef CONSOLE_ENABLE usbPoll(); diff --git a/tmk_core/protocol/vusb/vusb.c b/tmk_core/protocol/vusb/vusb.c index ebde955d3b4d..ab5f447fd8ca 100644 --- a/tmk_core/protocol/vusb/vusb.c +++ b/tmk_core/protocol/vusb/vusb.c @@ -35,6 +35,11 @@ along with this program. If not, see . # include "raw_hid.h" #endif +#ifdef XAP_ENABLE +# include "xap.h" +# include +#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,68 @@ 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, uint8_t response_flags, const void *data, size_t length) { + uint8_t rdata[XAP_BUFFER_SIZE] = {0}; + *(xap_token_t *)&rdata[0] = token; + if (length > (XAP_BUFFER_SIZE - 4)) response_flags &= ~(XAP_RESPONSE_FLAG_SUCCESS); + rdata[2] = response_flags; + if (response_flags & (XAP_RESPONSE_FLAG_SUCCESS)) { + rdata[3] = (uint8_t)length; + if (data != NULL) { + memcpy(&rdata[4], data, length); + } + } + xap_send_base(rdata, sizeof(rdata)); +} + +void xap_receive_base(const void *data) { + const uint8_t *u8data = (const uint8_t *)data; + xap_token_t token = *(xap_token_t *)&u8data[0]; + uint8_t length = u8data[2]; + if (length <= (XAP_BUFFER_SIZE - 3)) { + xap_receive(token, &u8data[3], 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 +473,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 } /*------------------------------------------------------------------* @@ -624,6 +713,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) @@ -823,6 +935,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 @@ -975,6 +1137,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; @@ -1007,6 +1176,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; diff --git a/tmk_core/protocol/vusb/vusb.h b/tmk_core/protocol/vusb/vusb.h index b1ecc98f3711..152d3fafe21a 100644 --- a/tmk_core/protocol/vusb/vusb.h +++ b/tmk_core/protocol/vusb/vusb.h @@ -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; From 70c9905cb647c345b7d8dda479c1c16f6776b1b5 Mon Sep 17 00:00:00 2001 From: zvecr Date: Fri, 18 Mar 2022 21:43:01 +0000 Subject: [PATCH 020/203] clang --- tmk_core/protocol/vusb/vusb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmk_core/protocol/vusb/vusb.c b/tmk_core/protocol/vusb/vusb.c index ab5f447fd8ca..8e120b4ca950 100644 --- a/tmk_core/protocol/vusb/vusb.c +++ b/tmk_core/protocol/vusb/vusb.c @@ -206,7 +206,7 @@ void xap_send_base(uint8_t *data, uint8_t length) { void xap_send(xap_token_t token, uint8_t response_flags, const void *data, size_t length) { uint8_t rdata[XAP_BUFFER_SIZE] = {0}; - *(xap_token_t *)&rdata[0] = token; + *(xap_token_t *)&rdata[0] = token; if (length > (XAP_BUFFER_SIZE - 4)) response_flags &= ~(XAP_RESPONSE_FLAG_SUCCESS); rdata[2] = response_flags; if (response_flags & (XAP_RESPONSE_FLAG_SUCCESS)) { From b365cbce15f0532312f49a09ef782ed081561f3d Mon Sep 17 00:00:00 2001 From: zvecr Date: Sun, 20 Mar 2022 01:25:04 +0000 Subject: [PATCH 021/203] Merge in keymap level to XAP info.json payload --- lib/python/qmk/cli/xap/generate_qmk.py | 42 +++------------ lib/python/qmk/info.py | 53 +++++++++++++++---- .../qmk/xap/gen_firmware/info_generator.py | 40 ++++++++++++++ 3 files changed, 91 insertions(+), 44 deletions(-) create mode 100644 lib/python/qmk/xap/gen_firmware/info_generator.py diff --git a/lib/python/qmk/cli/xap/generate_qmk.py b/lib/python/qmk/cli/xap/generate_qmk.py index ff43e2863d3b..26476dba46f3 100755 --- a/lib/python/qmk/cli/xap/generate_qmk.py +++ b/lib/python/qmk/cli/xap/generate_qmk.py @@ -1,19 +1,13 @@ """This script generates the XAP protocol generated sources to be compiled into QMK firmware. """ -import json -import gzip - from milc import cli from qmk.path import normpath -from qmk.info import info_json -from qmk.commands import get_chunks, dump_lines from qmk.keyboard import keyboard_completer, keyboard_folder +from qmk.xap.gen_firmware.info_generator import generate_info from qmk.xap.gen_firmware.inline_generator import generate_inline from qmk.xap.gen_firmware.header_generator import generate_header -from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE - @cli.argument('-o', '--output', type=normpath, help='File to write to') @cli.subcommand('Generates the XAP protocol include.', hidden=False if cli.config.user.developer else True) @@ -44,36 +38,14 @@ def xap_generate_qmk_h(cli): def xap_generate_info_h(cli): """Generates the XAP info.json payload header file, generated during normal build. """ - # Determine our keyboard + # 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 - # TODO: merge in keymap level content - # Build the info.json file - kb_info_json = info_json(cli.args.keyboard) - - # Minify - str_data = json.dumps(kb_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('unsigned char info_json_gz[] = {') - lines.append(data) - lines.append('};') - lines.append(f'unsigned int info_json_gz_len = {data_len};') - - dump_lines(cli.args.output, lines) + generate_info(cli.args.output, cli.args.keyboard, cli.args.keymap) diff --git a/lib/python/qmk/info.py b/lib/python/qmk/info.py index c399a9f32198..4a8a64a40af7 100644 --- a/lib/python/qmk/info.py +++ b/lib/python/qmk/info.py @@ -8,10 +8,10 @@ from milc import cli from qmk.constants import CHIBIOS_PROCESSORS, LUFA_PROCESSORS, VUSB_PROCESSORS -from qmk.c_parse import find_layouts +from qmk.c_parse import find_layouts, parse_config_h_file from qmk.json_schema import deep_update, json_load, validate from qmk.keyboard import config_h, rules_mk -from qmk.keymap import list_keymaps +from qmk.keymap import list_keymaps, locate_keymap from qmk.makefile import parse_rules_mk_file from qmk.math import compute @@ -68,8 +68,8 @@ def info_json(keyboard): # Merge in the data from info.json, config.h, and rules.mk info_data = merge_info_jsons(keyboard, info_data) - info_data = _extract_rules_mk(info_data) - info_data = _extract_config_h(info_data) + info_data = _extract_rules_mk(info_data, rules_mk(str(keyboard))) + info_data = _extract_config_h(info_data, config_h(str(keyboard))) # Ensure that we have matrix row and column counts info_data = _matrix_size(info_data) @@ -257,6 +257,10 @@ def _extract_split_main(info_data, config_c): def _extract_split_transport(info_data, config_c): + # TODO: Ignore? + if 'split' not in info_data: + return + # Figure out the transport method if config_c.get('USE_I2C') is True: if 'split' not in info_data: @@ -400,11 +404,9 @@ def _extract_device_version(info_data): info_data['usb']['device_version'] = f'{major}.{minor}.{revision}' -def _extract_config_h(info_data): +def _extract_config_h(info_data, config_c): """Pull some keyboard information from existing config.h files """ - config_c = config_h(info_data['keyboard_folder']) - # Pull in data from the json map dotty_info = dotty(info_data) info_config_map = json_load(Path('data/mappings/info_config.json')) @@ -472,10 +474,9 @@ def _extract_config_h(info_data): return info_data -def _extract_rules_mk(info_data): +def _extract_rules_mk(info_data, rules): """Pull some keyboard information from existing rules.mk files """ - rules = rules_mk(info_data['keyboard_folder']) info_data['processor'] = rules.get('MCU', info_data.get('processor', 'atmega32u4')) if info_data['processor'] in CHIBIOS_PROCESSORS: @@ -766,3 +767,37 @@ def find_info_json(keyboard): # Return a list of the info.json files that actually exist return [info_json for info_json in info_jsons if info_json.exists()] + + +def parse_keymap_json_file(file): + """load a valid keymap.json + """ + if not file.exists(): + return {} + km_info_json = json_load(file) + validate(km_info_json, 'qmk.keymap.v1') + return km_info_json + + +def keymap_json(keyboard, keymap): + """Generate the info.json data for a specific keymap. + """ + keymap_folder = locate_keymap(keyboard, keymap).parent + + # Files to scan + keymap_config = keymap_folder / 'config.h' + keymap_rules = keymap_folder / 'rules.mk' + keymap_file = keymap_folder / 'keymap.json' + + # Build the info.json file + kb_info_json = info_json(keyboard) + + # Merge in the data from keymap.json + km_info_json = parse_keymap_json_file(keymap_file).get('config', {}) + deep_update(kb_info_json, km_info_json) + + # Merge in the data from config.h, and rules.mk + _extract_rules_mk(kb_info_json, parse_rules_mk_file(keymap_rules)) + _extract_config_h(kb_info_json, parse_config_h_file(keymap_config)) + + return kb_info_json diff --git a/lib/python/qmk/xap/gen_firmware/info_generator.py b/lib/python/qmk/xap/gen_firmware/info_generator.py new file mode 100644 index 000000000000..b90bd5767249 --- /dev/null +++ b/lib/python/qmk/xap/gen_firmware/info_generator.py @@ -0,0 +1,40 @@ +"""This script generates the XAP info.json payload header to be compiled into QMK. +""" +import json +import gzip + +from qmk.info import keymap_json +from qmk.commands import get_chunks, dump_lines + +from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE + + +def generate_info(output_file, keyboard, keymap): + # Build the info.json file + km_info_json = keymap_json(keyboard, keymap) + + # TODO: Munge to XAP requirements + + # Minify + str_data = json.dumps(km_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('unsigned char info_json_gz[] = {') + lines.append(data) + lines.append('};') + lines.append(f'unsigned int info_json_gz_len = {data_len};') + + dump_lines(output_file, lines) From e31c605bf75826e5a5a243a264faa21e8e5bc601 Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 22 Mar 2022 00:04:12 +0000 Subject: [PATCH 022/203] revert split logic --- lib/python/qmk/info.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/python/qmk/info.py b/lib/python/qmk/info.py index 137a488bc8cb..d104d655ef0a 100644 --- a/lib/python/qmk/info.py +++ b/lib/python/qmk/info.py @@ -258,10 +258,6 @@ def _extract_split_main(info_data, config_c): def _extract_split_transport(info_data, config_c): - # TODO: Ignore? - if 'split' not in info_data: - return - # Figure out the transport method if config_c.get('USE_I2C') is True: if 'split' not in info_data: From ff1bb7653756a6997f255af26686cf32f6cc5388 Mon Sep 17 00:00:00 2001 From: zvecr Date: Mon, 28 Mar 2022 21:06:11 +0100 Subject: [PATCH 023/203] basic info.json handling --- data/xap/xap_0.1.0.hjson | 18 +++ lib/python/qmk/cli/xap/__init__.py | 80 +--------- lib/python/qmk/cli/xap/xap.py | 149 ++++++++++++++++++ .../qmk/xap/gen_firmware/info_generator.py | 5 +- .../qmk/xap/gen_firmware/inline_generator.py | 14 ++ quantum/xap/xap.c | 10 ++ quantum/xap/xap_handlers.c | 16 +- 7 files changed, 210 insertions(+), 82 deletions(-) create mode 100644 lib/python/qmk/cli/xap/xap.py diff --git a/data/xap/xap_0.1.0.hjson b/data/xap/xap_0.1.0.hjson index f74f17733990..8fdbcc02a67c 100755 --- a/data/xap/xap_0.1.0.hjson +++ b/data/xap/xap_0.1.0.hjson @@ -218,6 +218,24 @@ return_type: string return_constant: QSTR(PRODUCT) } + 0x05: { + type: command + name: info.json length + define: INFO_LEN_QUERY + description: Retrieves the length of info.json + return_type: u32 + return_constant: info_json_gz_len + } + 0x06: { + type: command + name: info.json + define: INFO_QUERY + description: Retrieves a chunk of info.json + request_type: u16 + request_purpose: offset + return_type: u8[32] + return_execute: info_json_gz + } } }, diff --git a/lib/python/qmk/cli/xap/__init__.py b/lib/python/qmk/cli/xap/__init__.py index 963617a6eeeb..190f3607ecec 100755 --- a/lib/python/qmk/cli/xap/__init__.py +++ b/lib/python/qmk/cli/xap/__init__.py @@ -1,79 +1 @@ -"""Interactions with compatible XAP devices -""" -import json -import random - -from milc import cli - - -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(cli.args.device.lower()) - - -def _search(): - devices = filter(_is_xap_usage, hid.enumerate()) - if cli.args.device: - devices = filter(_is_filtered_device, devices) - - return list(devices) - - -def _list_devices(): - """Dump out available devices - """ - cli.log.info('Available devices:') - devices = _search() - for dev in devices: - device = hid.Device(path=dev['path']) - - data = _query_device(device) - cli.log.info(" %04x:%04x %s %s [API:%s]", dev['vendor_id'], dev['product_id'], dev['manufacturer_string'], dev['product_string'], data['ver']) - - if cli.config.general.verbose: - # TODO: better formatting like "lsusb -v" - cli.log.info(" " + json.dumps(data)) - - -def _query_device(device): - # gen token - tok = random.getrandbits(16) - temp = tok.to_bytes(2, byteorder='big') - - # send with padding - padding = b"\x00" * 59 - device.write(temp + b'\x02\x00\x00' + padding) - - # get resp - array_alpha = device.read(8, 100) - # hex_string = " ".join("%02x" % b for b in array_alpha) - - # validate tok sent == resp - ver = "UNKNOWN" - if str(temp) == str(array_alpha[:2]): - # to BCD string - a = (array_alpha[7] << 24) + (array_alpha[6] << 16) + (array_alpha[5] << 8) + (array_alpha[4]) - ver = f'{a>>24}.{a>>16 & 0xFF}.{a & 0xFFFF}' - - return {'ver': ver} - - -@cli.argument('-d', '--device', help='device to select - uses format :.') -@cli.argument('-i', '--index', default=0, help='device index to select.') -@cli.argument('-l', '--list', arg_only=True, action='store_true', help='List available devices.') -@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 - """ - # Lazy load to avoid issues - global hid - import hid - - if cli.args.list: - return _list_devices() - - cli.log.warn("TODO: Device specific stuff") +from .xap import xap diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py new file mode 100644 index 000000000000..8aab20864cee --- /dev/null +++ b/lib/python/qmk/cli/xap/xap.py @@ -0,0 +1,149 @@ +"""Interactions with compatible XAP devices +""" +import json +import random +import gzip + +from milc import cli + + +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(cli.args.device.lower()) + + +def _search(): + devices = filter(_is_xap_usage, hid.enumerate()) + if cli.args.device: + devices = filter(_is_filtered_device, devices) + + return list(devices) + + +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], dict): + print_dotted_output(kb_info_json[key], new_prefix) + elif isinstance(kb_info_json[key], list): + cli.echo(' {fg_blue}%s{fg_reset}: %s', new_prefix, ', '.join(map(str, sorted(kb_info_json[key])))) + 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 = _search() + for dev in devices: + device = hid.Device(path=dev['path']) + + data = _query_device_version(device) + cli.log.info(" %04x:%04x %s %s [API:%s]", dev['vendor_id'], dev['product_id'], dev['manufacturer_string'], dev['product_string'], data['ver']) + + if cli.config.general.verbose: + # TODO: better formatting like "lsusb -v" + datalen = _query_device_info_len(device) + + data = [] + offset = 0 + while offset < datalen: + data += _query_device_info(device, offset) + offset += 32 + str_data = gzip.decompress(bytearray(data[:datalen])) + print_dotted_output(json.loads(str_data)) + + +def _query_device_version(device): + # gen token + tok = random.getrandbits(16) + temp = tok.to_bytes(2, byteorder='big') + + # send with padding + padding = b"\x00" * 59 + device.write(temp + b'\x02\x00\x00' + padding) + + # get resp + array_alpha = device.read(8, 100) + # hex_string = " ".join("%02x" % b for b in array_alpha) + + # validate tok sent == resp + ver = "UNKNOWN" + if str(temp) == str(array_alpha[:2]): + # to BCD string + a = (array_alpha[7] << 24) + (array_alpha[6] << 16) + (array_alpha[5] << 8) + (array_alpha[4]) + ver = f'{a>>24}.{a>>16 & 0xFF}.{a & 0xFFFF}' + + return {'ver': ver} + + +def _query_device_info_len(device): + # gen token + tok = random.getrandbits(16) + temp = tok.to_bytes(2, byteorder='big') + + # send with padding + padding = b"\x00" * 59 + device.write(temp + b'\x02\x01\x05' + padding) + + # get resp + array_alpha = device.read(8, 100) + # hex_string = " ".join("%02x" % b for b in array_alpha) + + # validate tok sent == resp + datalen = "UNKNOWN" + if str(temp) == str(array_alpha[:2]): + # to BCD string + a = (array_alpha[7] << 24) + (array_alpha[6] << 16) + (array_alpha[5] << 8) + (array_alpha[4]) + datalen = f'{a & 0xFFFF}' + + return int(datalen) + + +def _query_device_info(device, offset): + # gen token + tok = random.getrandbits(16) + temp = tok.to_bytes(2, byteorder='big') + + # send with padding + padding = b"\x00" * 57 + device.write(temp + b'\x04\x01\x06' + (offset).to_bytes(2, byteorder='big') + padding) + + # get resp + array_alpha = device.read(4 + 32, 100) + + # hex_string = " ".join("%02x" % b for b in array_alpha) + + # validate tok sent == resp + if str(temp) == str(array_alpha[:2]): + return array_alpha[4:] + return None + + +@cli.argument('-d', '--device', help='device to select - uses format :.') +@cli.argument('-i', '--index', default=0, help='device index to select.') +@cli.argument('-l', '--list', arg_only=True, action='store_true', help='List available devices.') +@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 + """ + # Lazy load to avoid issues + global hid + import hid + + if cli.args.list: + return _list_devices() + + cli.log.warn("TODO: Device specific stuff") diff --git a/lib/python/qmk/xap/gen_firmware/info_generator.py b/lib/python/qmk/xap/gen_firmware/info_generator.py index b90bd5767249..c25d82181b9c 100644 --- a/lib/python/qmk/xap/gen_firmware/info_generator.py +++ b/lib/python/qmk/xap/gen_firmware/info_generator.py @@ -14,6 +14,7 @@ def generate_info(output_file, keyboard, keymap): km_info_json = keymap_json(keyboard, keymap) # TODO: Munge to XAP requirements + del km_info_json['config_h_features'] # Minify str_data = json.dumps(km_info_json, separators=(',', ':')) @@ -32,9 +33,9 @@ def generate_info(output_file, keyboard, keymap): lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', ''] # Gen output file - lines.append('unsigned char info_json_gz[] = {') + lines.append('static const unsigned char info_json_gz[] PROGMEM = {') lines.append(data) lines.append('};') - lines.append(f'unsigned int info_json_gz_len = {data_len};') + lines.append(f'static const unsigned int info_json_gz_len = {data_len};') dump_lines(output_file, lines) diff --git a/lib/python/qmk/xap/gen_firmware/inline_generator.py b/lib/python/qmk/xap/gen_firmware/inline_generator.py index a40e40ec984e..a78d2f357eda 100755 --- a/lib/python/qmk/xap/gen_firmware/inline_generator.py +++ b/lib/python/qmk/xap/gen_firmware/inline_generator.py @@ -26,6 +26,8 @@ def _get_c_type(xap_type): def _get_route_type(container): if 'routes' in container: return 'XAP_ROUTE' + elif 'return_execute' in container: + return 'XAP_EXECUTE' elif 'return_constant' in container: if container['return_type'] == 'u32': return 'XAP_VALUE' @@ -47,6 +49,11 @@ def _append_routing_table_declaration(lines, container, container_id, route_stac if 'routes' in container: pass + elif 'return_execute' in container: + execute = container['return_execute'] + lines.append('') + lines.append(f'bool xap_respond_{execute}(xap_token_t token, const uint8_t *data, size_t data_len);') + elif 'return_constant' in container: if container['return_type'] == 'u32': @@ -99,6 +106,11 @@ def _append_routing_table_entry_route(lines, container, container_id, route_stac 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_u32value(lines, container, container_id, route_stack): value = container['return_constant'] lines.append(f' .u32value = {value},') @@ -134,6 +146,8 @@ def _append_routing_table_entry(lines, container, container_id, route_stack): _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_constant' in container: if container['return_type'] == 'u32': _append_routing_table_entry_u32value(lines, container, container_id, route_stack) diff --git a/quantum/xap/xap.c b/quantum/xap/xap.c index b348d366fb76..f468eac9b414 100644 --- a/quantum/xap/xap.c +++ b/quantum/xap/xap.c @@ -17,6 +17,16 @@ #include #include +#include "info_json_gz.h" +void get_info_json_chunk(uint8_t *data, size_t offset) { + uint8_t len = 32; + if (offset + len > info_json_gz_len) { + len = info_json_gz_len - offset; + } + + memcpy_P(data, &info_json_gz[offset], len); +} + #define QSTR2(z) #z #define QSTR(z) QSTR2(z) diff --git a/quantum/xap/xap_handlers.c b/quantum/xap/xap_handlers.c index 492d3d25bc02..f21cdab3160b 100644 --- a/quantum/xap/xap_handlers.c +++ b/quantum/xap/xap_handlers.c @@ -16,7 +16,6 @@ #include #include -#include void xap_respond_failure(xap_token_t token, xap_response_flags_t response_flags) { xap_send(token, response_flags, NULL, 0); @@ -41,3 +40,18 @@ bool xap_respond_u32(xap_token_t token, uint32_t value) { uint32_t xap_route_qmk_ffffffffffffffff_getter(void) { return 0x12345678; } + +bool xap_respond_info_json_gz(xap_token_t token, const uint8_t *data, size_t data_len) { + if (data_len != 2) { + xap_respond_failure(token, 0); + return false; + } + + uint8_t blob[32] = {0}; + uint16_t offset = ((uint16_t)data[0]) << 8 | data[1]; + + void get_info_json_chunk(uint8_t * data, size_t offset); + get_info_json_chunk(blob, offset); + + return xap_respond_data(token, blob, 32); +} From 7262333857e2c006c108e075d4b76f088b27a379 Mon Sep 17 00:00:00 2001 From: zvecr Date: Mon, 28 Mar 2022 21:18:17 +0100 Subject: [PATCH 024/203] Use generic 'dump_lines' --- .../qmk/xap/gen_firmware/header_generator.py | 18 +++--------------- .../qmk/xap/gen_firmware/inline_generator.py | 17 ++--------------- 2 files changed, 5 insertions(+), 30 deletions(-) diff --git a/lib/python/qmk/xap/gen_firmware/header_generator.py b/lib/python/qmk/xap/gen_firmware/header_generator.py index ea299b18f568..bb0df0595922 100755 --- a/lib/python/qmk/xap/gen_firmware/header_generator.py +++ b/lib/python/qmk/xap/gen_firmware/header_generator.py @@ -3,6 +3,7 @@ import re from fnvhash import fnv1a_32 +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 latest_xap_defs, route_conditions @@ -119,18 +120,5 @@ def generate_header(output_file, keyboard): _append_route_capabilities(lines, xap_defs) lines.append('') - # Generate the full output - xap_generated_inl = '\n'.join(lines) - - # Clean up newlines - while "\n\n\n" in xap_generated_inl: - xap_generated_inl = xap_generated_inl.replace("\n\n\n", "\n\n") - - if output_file: - if output_file.name == '-': - print(xap_generated_inl) - else: - output_file.parent.mkdir(parents=True, exist_ok=True) - if output_file.exists(): - output_file.replace(output_file.parent / (output_file.name + '.bak')) - output_file.write_text(xap_generated_inl) + dump_lines(output_file, lines) + diff --git a/lib/python/qmk/xap/gen_firmware/inline_generator.py b/lib/python/qmk/xap/gen_firmware/inline_generator.py index a78d2f357eda..8563bfdc6905 100755 --- a/lib/python/qmk/xap/gen_firmware/inline_generator.py +++ b/lib/python/qmk/xap/gen_firmware/inline_generator.py @@ -1,6 +1,7 @@ """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 latest_xap_defs, route_conditions @@ -217,18 +218,4 @@ def generate_inline(output_file): # Add all the generated code _append_routing_tables(lines, xap_defs) - # Generate the full output - xap_generated_inl = '\n'.join(lines) - - # Clean up newlines - while "\n\n\n" in xap_generated_inl: - xap_generated_inl = xap_generated_inl.replace("\n\n\n", "\n\n") - - if output_file: - if output_file.name == '-': - print(xap_generated_inl) - else: - output_file.parent.mkdir(parents=True, exist_ok=True) - if output_file.exists(): - output_file.replace(output_file.parent / (output_file.name + '.bak')) - output_file.write_text(xap_generated_inl) + dump_lines(output_file, lines) From 05911e9908404f46b2df2324d669fbadadb05a49 Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 29 Mar 2022 18:36:08 +0100 Subject: [PATCH 025/203] bodge 'qmk xap -l' for windows --- lib/python/qmk/cli/xap/xap.py | 132 +++++++++++++++++----------------- 1 file changed, 66 insertions(+), 66 deletions(-) diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index 8aab20864cee..bfd9511711e1 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -3,6 +3,7 @@ import json import random import gzip +from platform import platform from milc import cli @@ -42,94 +43,93 @@ def print_dotted_output(kb_info_json, prefix=''): 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 = _search() - for dev in devices: - device = hid.Device(path=dev['path']) - - data = _query_device_version(device) - cli.log.info(" %04x:%04x %s %s [API:%s]", dev['vendor_id'], dev['product_id'], dev['manufacturer_string'], dev['product_string'], data['ver']) - - if cli.config.general.verbose: - # TODO: better formatting like "lsusb -v" - datalen = _query_device_info_len(device) - - data = [] - offset = 0 - while offset < datalen: - data += _query_device_info(device, offset) - offset += 32 - str_data = gzip.decompress(bytearray(data[:datalen])) - print_dotted_output(json.loads(str_data)) - - -def _query_device_version(device): +def _xap_transaction(device, sub, route, ret_len, *args): # gen token tok = random.getrandbits(16) - temp = tok.to_bytes(2, byteorder='big') + token = tok.to_bytes(2, byteorder='big') # send with padding - padding = b"\x00" * 59 - device.write(temp + b'\x02\x00\x00' + padding) + # TODO: this code is total garbage + args_data = [] + args_len = 2 + if len(args) == 1: + args_len += 2 + args_data = args[0].to_bytes(2, byteorder='big') + + padding = b"\x00" * (64 - 3 - args_len) + if args_data: + padding = args_data + padding + buffer = token + args_len.to_bytes(1, byteorder='big') + sub.to_bytes(1, byteorder='big') + route.to_bytes(1, byteorder='big') + padding + + # prepend 0 on windows because reasons... + if 'windows' in platform().lower(): + buffer = b"\x00" + buffer + + device.write(buffer) # get resp - array_alpha = device.read(8, 100) - # hex_string = " ".join("%02x" % b for b in array_alpha) + array_alpha = device.read(4 + ret_len, 100) # validate tok sent == resp - ver = "UNKNOWN" - if str(temp) == str(array_alpha[:2]): - # to BCD string - a = (array_alpha[7] << 24) + (array_alpha[6] << 16) + (array_alpha[5] << 8) + (array_alpha[4]) - ver = f'{a>>24}.{a>>16 & 0xFF}.{a & 0xFFFF}' + if str(token) != str(array_alpha[:2]): + return None + return array_alpha[4:] + + +def _query_device_version(device): + ver_data = _xap_transaction(device, 0x00, 0x00, 4) + if not ver_data: + return {'xap': 'UNKNOWN'} + + # to u32 to BCD string + a = (ver_data[3] << 24) + (ver_data[2] << 16) + (ver_data[1] << 8) + (ver_data[0]) + ver = f'{a>>24}.{a>>16 & 0xFF}.{a & 0xFFFF}' - return {'ver': ver} + return {'xap': ver} def _query_device_info_len(device): - # gen token - tok = random.getrandbits(16) - temp = tok.to_bytes(2, byteorder='big') + len_data = _xap_transaction(device, 0x01, 0x05, 4) + if not len_data: + return 0 - # send with padding - padding = b"\x00" * 59 - device.write(temp + b'\x02\x01\x05' + padding) + # to u32 + return (len_data[3] << 24) + (len_data[2] << 16) + (len_data[1] << 8) + (len_data[0]) - # get resp - array_alpha = device.read(8, 100) - # hex_string = " ".join("%02x" % b for b in array_alpha) - # validate tok sent == resp - datalen = "UNKNOWN" - if str(temp) == str(array_alpha[:2]): - # to BCD string - a = (array_alpha[7] << 24) + (array_alpha[6] << 16) + (array_alpha[5] << 8) + (array_alpha[4]) - datalen = f'{a & 0xFFFF}' +def _query_device_info_chunk(device, offset): + return _xap_transaction(device, 0x01, 0x06, 32, offset) - return int(datalen) +def _query_device_info(device): + datalen = _query_device_info_len(device) + if not datalen: + return {} -def _query_device_info(device, offset): - # gen token - tok = random.getrandbits(16) - temp = tok.to_bytes(2, byteorder='big') + data = [] + offset = 0 + while offset < datalen: + data += _query_device_info_chunk(device, offset) + offset += 32 + str_data = gzip.decompress(bytearray(data[:datalen])) + return json.loads(str_data) - # send with padding - padding = b"\x00" * 57 - device.write(temp + b'\x04\x01\x06' + (offset).to_bytes(2, byteorder='big') + padding) - # get resp - array_alpha = device.read(4 + 32, 100) +def _list_devices(): + """Dump out available devices + """ + cli.log.info('Available devices:') + devices = _search() + for dev in devices: + device = hid.Device(path=dev['path']) - # hex_string = " ".join("%02x" % b for b in array_alpha) + data = _query_device_version(device) + cli.log.info(" %04x:%04x %s %s [API:%s]", dev['vendor_id'], dev['product_id'], dev['manufacturer_string'], dev['product_string'], data['xap']) - # validate tok sent == resp - if str(temp) == str(array_alpha[:2]): - return array_alpha[4:] - return None + if cli.config.general.verbose: + # TODO: better formatting like "lsusb -v"? + data = _query_device_info(device) + print_dotted_output(data) @cli.argument('-d', '--device', help='device to select - uses format :.') From 56c9f7b7ff23196afba65b66f632fcc10070175d Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 29 Mar 2022 19:25:16 +0100 Subject: [PATCH 026/203] format --- lib/python/qmk/cli/xap/xap.py | 5 +++-- lib/python/qmk/xap/gen_firmware/header_generator.py | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index bfd9511711e1..7826ed33d661 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -56,11 +56,12 @@ def _xap_transaction(device, sub, route, ret_len, *args): args_len += 2 args_data = args[0].to_bytes(2, byteorder='big') - padding = b"\x00" * (64 - 3 - args_len) + padding_len = 64 - 3 - args_len + padding = b"\x00" * padding_len if args_data: padding = args_data + padding buffer = token + args_len.to_bytes(1, byteorder='big') + sub.to_bytes(1, byteorder='big') + route.to_bytes(1, byteorder='big') + padding - + # prepend 0 on windows because reasons... if 'windows' in platform().lower(): buffer = b"\x00" + buffer diff --git a/lib/python/qmk/xap/gen_firmware/header_generator.py b/lib/python/qmk/xap/gen_firmware/header_generator.py index bb0df0595922..42d54e1960ce 100755 --- a/lib/python/qmk/xap/gen_firmware/header_generator.py +++ b/lib/python/qmk/xap/gen_firmware/header_generator.py @@ -121,4 +121,3 @@ def generate_header(output_file, keyboard): lines.append('') dump_lines(output_file, lines) - From 13ee88dd21f9cfc051ad064a1c90019db1159f0e Mon Sep 17 00:00:00 2001 From: zvecr Date: Fri, 25 Mar 2022 02:13:31 +0000 Subject: [PATCH 027/203] Data driven g_led config --- builddefs/build_keyboard.mk | 8 +- data/schemas/keyboard.jsonschema | 56 +++++++++++ lib/python/qmk/c_parse.py | 116 ++++++++++++++++++++++ lib/python/qmk/cli/__init__.py | 1 + lib/python/qmk/cli/generate/keyboard_c.py | 65 ++++++++++++ lib/python/qmk/info.py | 45 ++++++++- lib/python/qmk/json_encoders.py | 4 +- 7 files changed, 291 insertions(+), 4 deletions(-) create mode 100755 lib/python/qmk/cli/generate/keyboard_c.py diff --git a/builddefs/build_keyboard.mk b/builddefs/build_keyboard.mk index 44acd964cc7a..7c8f96663784 100644 --- a/builddefs/build_keyboard.mk +++ b/builddefs/build_keyboard.mk @@ -328,12 +328,18 @@ ifneq ("$(wildcard $(KEYBOARD_PATH_5)/info.json)","") endif CONFIG_H += $(KEYBOARD_OUTPUT)/src/info_config.h $(KEYBOARD_OUTPUT)/src/layouts.h +KEYBOARD_SRC += $(KEYBOARD_OUTPUT)/src/default_keyboard.c $(KEYBOARD_OUTPUT)/src/info_config.h: $(INFO_JSON_FILES) @$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD) $(eval CMD=$(QMK_BIN) generate-config-h --quiet --keyboard $(KEYBOARD) --output $(KEYBOARD_OUTPUT)/src/info_config.h) @$(BUILD_CMD) +$(KEYBOARD_OUTPUT)/src/default_keyboard.c: $(INFO_JSON_FILES) + @$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD) + $(eval CMD=$(QMK_BIN) generate-keyboard-c --quiet --keyboard $(KEYBOARD) --output $(KEYBOARD_OUTPUT)/src/default_keyboard.c) + @$(BUILD_CMD) + $(KEYBOARD_OUTPUT)/src/default_keyboard.h: $(INFO_JSON_FILES) @$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD) $(eval CMD=$(QMK_BIN) generate-keyboard-h --quiet --keyboard $(KEYBOARD) --output $(KEYBOARD_OUTPUT)/src/default_keyboard.h) @@ -344,7 +350,7 @@ $(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.h $(KEYBOARD_OUTPUT)/src/layouts.h +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 .INTERMEDIATE : generated-files diff --git a/data/schemas/keyboard.jsonschema b/data/schemas/keyboard.jsonschema index a8b3d0693384..28207416914c 100644 --- a/data/schemas/keyboard.jsonschema +++ b/data/schemas/keyboard.jsonschema @@ -193,6 +193,62 @@ "timeout": {"$ref": "qmk.definitions.v1#/unsigned_int"} } }, + "led_matrix": { + "type": "object", + "properties": { + "driver": {"type": "string"}, + "layout": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "matrix": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "number", + "min": 0, + "multipleOf": 1 + } + }, + "x": {"$ref": "qmk.definitions.v1#/key_unit"}, + "y": {"$ref": "qmk.definitions.v1#/key_unit"}, + "flags": {"$ref": "qmk.definitions.v1#/unsigned_decimal"} + } + } + } + } + }, + "rgb_matrix": { + "type": "object", + "properties": { + "driver": {"type": "string"}, + "layout": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "matrix": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "number", + "min": 0, + "multipleOf": 1 + } + }, + "x": {"$ref": "qmk.definitions.v1#/key_unit"}, + "y": {"$ref": "qmk.definitions.v1#/key_unit"}, + "flags": {"$ref": "qmk.definitions.v1#/unsigned_decimal"} + } + } + } + } + }, "rgblight": { "type": "object", "additionalProperties": false, diff --git a/lib/python/qmk/c_parse.py b/lib/python/qmk/c_parse.py index 72be690019fa..dcee19cbdf4b 100644 --- a/lib/python/qmk/c_parse.py +++ b/lib/python/qmk/c_parse.py @@ -1,5 +1,9 @@ """Functions for working with config.h files. """ +from pygments.lexers.c_cpp import CLexer +from pygments.token import Token +from pygments import lex +from itertools import islice from pathlib import Path import re @@ -13,6 +17,13 @@ layout_macro_define_regex = re.compile(r'^#\s*define') +def _get_chunks(it, size): + """Break down a collection into smaller parts + """ + it = iter(it) + return iter(lambda: tuple(islice(it, size)), ()) + + def strip_line_comment(string): """Removes comments from a single line string. """ @@ -170,3 +181,108 @@ def _parse_matrix_locations(matrix, file, macro_name): matrix_locations[identifier] = [row_num, col_num] return matrix_locations + + +def _coerce_led_token(_type, value): + """ Convert token to valid info.json content + """ + value_map = { + 'NO_LED': None, + 'LED_FLAG_ALL': 0xFF, + 'LED_FLAG_NONE': 0x00, + 'LED_FLAG_MODIFIER': 0x01, + 'LED_FLAG_UNDERGLOW': 0x02, + 'LED_FLAG_KEYLIGHT': 0x04, + 'LED_FLAG_INDICATOR': 0x08, + } + if _type is Token.Literal.Number.Integer: + return int(value) + if _type is Token.Literal.Number.Hex: + return int(value, 0) + if _type is Token.Name and value in value_map.keys(): + return value_map[value] + + +def _parse_led_config(file, matrix_cols, matrix_rows): + """Return any 'raw' led/rgb matrix config + """ + file_contents = file.read_text(encoding='utf-8') + file_contents = comment_remover(file_contents) + file_contents = file_contents.replace('\\\n', '') + + matrix_raw = [] + position_raw = [] + flags = [] + + found_led_config = False + bracket_count = 0 + section = 0 + for _type, value in lex(file_contents, CLexer()): + # Assume g_led_config..stuff..; + if value == 'g_led_config': + found_led_config = True + elif value == ';': + found_led_config = False + elif found_led_config: + # Assume bracket count hints to section of config we are within + if value == '{': + bracket_count += 1 + if bracket_count == 2: + section += 1 + elif value == '}': + bracket_count -= 1 + else: + # Assume any non whitespace value here is important enough to stash + if _type in [Token.Literal.Number.Integer, Token.Literal.Number.Hex, Token.Name]: + if section == 1 and bracket_count == 3: + matrix_raw.append(_coerce_led_token(_type, value)) + if section == 2 and bracket_count == 3: + position_raw.append(_coerce_led_token(_type, value)) + if section == 3 and bracket_count == 2: + flags.append(_coerce_led_token(_type, value)) + + # Slightly better intrim format + matrix = list(_get_chunks(matrix_raw, matrix_cols)) + position = list(_get_chunks(position_raw, 2)) + matrix_indexes = list(filter(lambda x: x is not None, matrix_raw)) + + # If we have not found anything - bail + if not section: + return None + + # TODO: Improve crude parsing/validation + if len(matrix) != matrix_rows and len(matrix) != (matrix_rows / 2): + raise ValueError("Unable to parse g_led_config matrix data") + if len(position) != len(flags): + raise ValueError("Unable to parse g_led_config position data") + if len(matrix_indexes) and (max(matrix_indexes) >= len(flags)): + raise ValueError("OOB within g_led_config matrix data") + + return (matrix, position, flags) + + +def find_led_config(file, matrix_cols, matrix_rows): + """Search file for led/rgb matrix config + """ + found = _parse_led_config(file, matrix_cols, matrix_rows) + if not found: + return None + + # Expand collected content + (matrix, position, flags) = found + + # Align to output format + led_config = [] + for index, item in enumerate(position, start=0): + led_config.append({ + 'x': item[0], + 'y': item[1], + 'flags': flags[index], + }) + for r in range(len(matrix)): + for c in range(len(matrix[r])): + index = matrix[r][c] + if index is not None: + led_config[index]['matrix'] = [r, c] + + return led_config diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py index ee4321a2bb8d..6e25c8978031 100644 --- a/lib/python/qmk/cli/__init__.py +++ b/lib/python/qmk/cli/__init__.py @@ -52,6 +52,7 @@ 'qmk.cli.generate.dfu_header', 'qmk.cli.generate.docs', 'qmk.cli.generate.info_json', + 'qmk.cli.generate.keyboard_c', 'qmk.cli.generate.keyboard_h', 'qmk.cli.generate.layouts', 'qmk.cli.generate.rgb_breathe_table', diff --git a/lib/python/qmk/cli/generate/keyboard_c.py b/lib/python/qmk/cli/generate/keyboard_c.py new file mode 100755 index 000000000000..7f74eb9b42ed --- /dev/null +++ b/lib/python/qmk/cli/generate/keyboard_c.py @@ -0,0 +1,65 @@ +"""Used by the make system to generate keyboard.c from info.json. +""" +from milc import cli + +from qmk.info import info_json +from qmk.commands import dump_lines +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 + + +def _gen_led_config(info_data): + """Convert info.json content to g_led_config + """ + cols = info_data['matrix_size']['cols'] + rows = info_data['matrix_size']['rows'] + + if 'layout' in info_data.get('rgb_matrix', {}): + led_config = info_data['rgb_matrix']['layout'] + elif 'layout' in info_data.get('led_matrix', {}): + led_config = info_data['led_matrix']['layout'] + + lines = [] + if not led_config: + return lines + + matrix = [['NO_PIN'] * cols for i in range(rows)] + pos = [] + flags = [] + + for index, item in enumerate(led_config, start=0): + if 'matrix' in item: + (x, y) = item['matrix'] + matrix[x][y] = str(index) + pos.append(f'{{ {item.get("x", 0)},{item.get("y", 0)} }}') + flags.append(str(item.get('flags', 0))) + + lines.append('__attribute__ ((weak)) led_config_t g_led_config = {') + lines.append(' {') + for line in matrix: + lines.append(f' {{ {",".join(line)} }},') + lines.append(' },') + lines.append(f' {{ {",".join(pos)} }},') + lines.append(f' {{ {",".join(flags)} }},') + lines.append('};') + + return lines + + +@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 keyboard.c for.') +@cli.subcommand('Used by the make system to generate keyboard.c from info.json', hidden=True) +def generate_keyboard_c(cli): + """Generates the keyboard.h file. + """ + kb_info_json = info_json(cli.args.keyboard) + + # Build the layouts.h file. + keyboard_h_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#include QMK_KEYBOARD_H', ''] + + keyboard_h_lines.extend(_gen_led_config(kb_info_json)) + + # Show the results + dump_lines(cli.args.output, keyboard_h_lines, cli.args.quiet) diff --git a/lib/python/qmk/info.py b/lib/python/qmk/info.py index 1d81b3e94b3c..3e896b90ee72 100644 --- a/lib/python/qmk/info.py +++ b/lib/python/qmk/info.py @@ -8,7 +8,7 @@ from milc import cli from qmk.constants import CHIBIOS_PROCESSORS, LUFA_PROCESSORS, VUSB_PROCESSORS -from qmk.c_parse import find_layouts, parse_config_h_file +from qmk.c_parse import find_layouts, parse_config_h_file, find_led_config from qmk.json_schema import deep_update, json_load, validate from qmk.keyboard import config_h, rules_mk from qmk.keymap import list_keymaps, locate_keymap @@ -75,6 +75,9 @@ def info_json(keyboard): # Ensure that we have matrix row and column counts info_data = _matrix_size(info_data) + # Merge in data from + info_data = _extract_led_config(info_data, str(keyboard)) + # Validate against the jsonschema try: validate(info_data, 'qmk.api.keyboard.v1') @@ -543,6 +546,46 @@ def _extract_rules_mk(info_data, rules): return info_data +def find_keyboard_c(keyboard): + """Find all .c files + """ + keyboard = Path(keyboard) + current_path = Path('keyboards/') + + files = [] + for directory in keyboard.parts: + current_path = current_path / directory + keyboard_c_path = current_path / f'{directory}.c' + if keyboard_c_path.exists(): + files.append(keyboard_c_path) + + return files + + +def _extract_led_config(info_data, keyboard): + """Scan all .c files for led config + """ + cols = info_data['matrix_size']['cols'] + rows = info_data['matrix_size']['rows'] + + # Assume what feature owns g_led_config + feature = "rgb_matrix" + if info_data.get("features", {}).get("led_matrix", False): + feature = "led_matrix" + + # Process + for file in find_keyboard_c(keyboard): + try: + ret = find_led_config(file, cols, rows) + if ret: + info_data[feature] = info_data.get(feature, {}) + info_data[feature]["layout"] = ret + except Exception as e: + _log_warning(info_data, f'led_config: {file.name}: {e}') + + return info_data + + def _matrix_size(info_data): """Add info_data['matrix_size'] if it doesn't exist. """ diff --git a/lib/python/qmk/json_encoders.py b/lib/python/qmk/json_encoders.py index 40a5c1dea8e7..f968b3dbb2d3 100755 --- a/lib/python/qmk/json_encoders.py +++ b/lib/python/qmk/json_encoders.py @@ -75,8 +75,8 @@ def encode_dict(self, obj): """Encode info.json dictionaries. """ if obj: - if self.indentation_level == 4: - # These are part of a layout, put them on a single line. + if set(("x", "y")).issubset(obj.keys()): + # These are part of a layout/led_config, put them on a single line. return "{ " + ", ".join(f"{self.encode(key)}: {self.encode(element)}" for key, element in sorted(obj.items())) + " }" else: From a65ea1a71158797e349416749ce4d48443ce9a5a Mon Sep 17 00:00:00 2001 From: zvecr Date: Sun, 27 Mar 2022 15:26:48 +0100 Subject: [PATCH 028/203] Fix codegen for non led boards --- lib/python/qmk/cli/generate/keyboard_c.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/python/qmk/cli/generate/keyboard_c.py b/lib/python/qmk/cli/generate/keyboard_c.py index 7f74eb9b42ed..c03c569c1634 100755 --- a/lib/python/qmk/cli/generate/keyboard_c.py +++ b/lib/python/qmk/cli/generate/keyboard_c.py @@ -15,6 +15,7 @@ def _gen_led_config(info_data): cols = info_data['matrix_size']['cols'] rows = info_data['matrix_size']['rows'] + led_config = None if 'layout' in info_data.get('rgb_matrix', {}): led_config = info_data['rgb_matrix']['layout'] elif 'layout' in info_data.get('led_matrix', {}): From 22b82992303f2edf9fc95abefc35d423fe80f144 Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 30 Mar 2022 00:43:25 +0100 Subject: [PATCH 029/203] Fix up print_dotted_output dict handling --- lib/python/qmk/cli/xap/xap.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index 7826ed33d661..13fc33de1a31 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -38,7 +38,12 @@ def print_dotted_output(kb_info_json, prefix=''): elif isinstance(kb_info_json[key], dict): print_dotted_output(kb_info_json[key], new_prefix) elif isinstance(kb_info_json[key], list): - cli.echo(' {fg_blue}%s{fg_reset}: %s', new_prefix, ', '.join(map(str, sorted(kb_info_json[key])))) + 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(sorted(map(str, data)))) else: cli.echo(' {fg_blue}%s{fg_reset}: %s', new_prefix, kb_info_json[key]) From 7f128c52865d6aa978ad74e0d96e3fd6ff78fdd7 Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 30 Mar 2022 02:09:19 +0100 Subject: [PATCH 030/203] Remove some assumptions on packet format --- quantum/xap/xap.h | 12 ++++++++++++ tmk_core/protocol/chibios/usb_main.c | 26 ++++++++++++++------------ tmk_core/protocol/lufa/lufa.c | 24 +++++++++++++----------- tmk_core/protocol/vusb/vusb.c | 24 +++++++++++++----------- 4 files changed, 52 insertions(+), 34 deletions(-) diff --git a/quantum/xap/xap.h b/quantum/xap/xap.h index f3cab6aef29b..67e41d31ea1e 100644 --- a/quantum/xap/xap.h +++ b/quantum/xap/xap.h @@ -41,4 +41,16 @@ 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); +// TODO: gen from xap defs? +typedef struct { + xap_token_t token; + uint8_t length; +} xap_request_header_t; + +typedef struct { + xap_token_t token; + xap_response_flags_t flags; + uint8_t length; +} xap_response_header_t; + #include diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index a95ca79da954..32de4622c148 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -113,7 +113,7 @@ static const USBDescriptor *usb_get_descriptor_cb(USBDriver *usbp, uint8_t dtype static USBDescriptor desc; uint16_t wValue = ((uint16_t)dtype << 8) | dindex; desc.ud_string = NULL; - desc.ud_size = get_usb_descriptor(wValue, wIndex, (const void **const) & desc.ud_string); + desc.ud_size = get_usb_descriptor(wValue, wIndex, (const void **const)&desc.ud_string); if (desc.ud_string == NULL) return NULL; else @@ -1138,25 +1138,27 @@ void xap_send_base(uint8_t *data, uint8_t length) { } void xap_send(xap_token_t token, uint8_t response_flags, const void *data, size_t length) { - uint8_t rdata[XAP_EPSIZE] = {0}; - *(xap_token_t *)&rdata[0] = token; - if (length > (XAP_EPSIZE - 4)) response_flags &= ~(XAP_RESPONSE_FLAG_SUCCESS); - rdata[2] = response_flags; + 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)) { - rdata[3] = (uint8_t)length; + header->length = (uint8_t)length; if (data != NULL) { - memcpy(&rdata[4], data, length); + memcpy(&rdata[sizeof(xap_response_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_token_t token = *(xap_token_t *)&u8data[0]; - uint8_t length = u8data[2]; - if (length <= (XAP_EPSIZE - 3)) { - xap_receive(token, &u8data[3], length); + 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); } } diff --git a/tmk_core/protocol/lufa/lufa.c b/tmk_core/protocol/lufa/lufa.c index 0dcf1036c48a..b3fe89c3191e 100644 --- a/tmk_core/protocol/lufa/lufa.c +++ b/tmk_core/protocol/lufa/lufa.c @@ -244,25 +244,27 @@ void xap_send_base(uint8_t *data, uint8_t length) { } void xap_send(xap_token_t token, uint8_t response_flags, const void *data, size_t length) { - uint8_t rdata[XAP_EPSIZE] = {0}; - *(xap_token_t *)&rdata[0] = token; - if (length > (XAP_EPSIZE - 4)) response_flags &= ~(XAP_RESPONSE_FLAG_SUCCESS); - rdata[2] = response_flags; + 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)) { - rdata[3] = (uint8_t)length; + header->length = (uint8_t)length; if (data != NULL) { - memcpy(&rdata[4], data, length); + memcpy(&rdata[sizeof(xap_response_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_token_t token = *(xap_token_t *)&u8data[0]; - uint8_t length = u8data[2]; - if (length <= (XAP_EPSIZE - 3)) { - xap_receive(token, &u8data[3], length); + 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); } } diff --git a/tmk_core/protocol/vusb/vusb.c b/tmk_core/protocol/vusb/vusb.c index 8e120b4ca950..6cf451144fd2 100644 --- a/tmk_core/protocol/vusb/vusb.c +++ b/tmk_core/protocol/vusb/vusb.c @@ -205,25 +205,27 @@ void xap_send_base(uint8_t *data, uint8_t length) { } void xap_send(xap_token_t token, uint8_t response_flags, const void *data, size_t length) { - uint8_t rdata[XAP_BUFFER_SIZE] = {0}; - *(xap_token_t *)&rdata[0] = token; - if (length > (XAP_BUFFER_SIZE - 4)) response_flags &= ~(XAP_RESPONSE_FLAG_SUCCESS); - rdata[2] = response_flags; + 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)) { - rdata[3] = (uint8_t)length; + header->length = (uint8_t)length; if (data != NULL) { - memcpy(&rdata[4], data, length); + memcpy(&rdata[sizeof(xap_response_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_token_t token = *(xap_token_t *)&u8data[0]; - uint8_t length = u8data[2]; - if (length <= (XAP_BUFFER_SIZE - 3)) { - xap_receive(token, &u8data[3], length); + 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); } } From 51e09235a2f477ec53972e15c70b439914d8d50d Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 30 Mar 2022 02:28:03 +0100 Subject: [PATCH 031/203] clang --- tmk_core/protocol/chibios/usb_main.c | 4 ++-- tmk_core/protocol/lufa/lufa.c | 2 +- tmk_core/protocol/vusb/vusb.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index 32de4622c148..53116d156219 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -113,7 +113,7 @@ static const USBDescriptor *usb_get_descriptor_cb(USBDriver *usbp, uint8_t dtype static USBDescriptor desc; uint16_t wValue = ((uint16_t)dtype << 8) | dindex; desc.ud_string = NULL; - desc.ud_size = get_usb_descriptor(wValue, wIndex, (const void **const)&desc.ud_string); + desc.ud_size = get_usb_descriptor(wValue, wIndex, (const void **const) & desc.ud_string); if (desc.ud_string == NULL) return NULL; else @@ -1155,7 +1155,7 @@ void xap_send(xap_token_t token, uint8_t response_flags, const void *data, size_ } void xap_receive_base(const void *data) { - const uint8_t *u8data = (const uint8_t *)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); diff --git a/tmk_core/protocol/lufa/lufa.c b/tmk_core/protocol/lufa/lufa.c index b3fe89c3191e..969bf1ab21ad 100644 --- a/tmk_core/protocol/lufa/lufa.c +++ b/tmk_core/protocol/lufa/lufa.c @@ -261,7 +261,7 @@ void xap_send(xap_token_t token, uint8_t response_flags, const void *data, size_ } void xap_receive_base(const void *data) { - const uint8_t *u8data = (const uint8_t *)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); diff --git a/tmk_core/protocol/vusb/vusb.c b/tmk_core/protocol/vusb/vusb.c index 6cf451144fd2..49d59f1574f9 100644 --- a/tmk_core/protocol/vusb/vusb.c +++ b/tmk_core/protocol/vusb/vusb.c @@ -222,7 +222,7 @@ void xap_send(xap_token_t token, uint8_t response_flags, const void *data, size_ } void xap_receive_base(const void *data) { - const uint8_t *u8data = (const uint8_t *)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); From 6269c6b51c0f1bcedd226a65f5efeeadb4b51815 Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 30 Mar 2022 23:19:56 +0100 Subject: [PATCH 032/203] partial gen for return_execute --- data/xap/xap_0.1.0.hjson | 2 +- lib/python/qmk/cli/xap/xap.py | 6 ++-- .../qmk/xap/gen_firmware/inline_generator.py | 36 +++++++++++++++++-- quantum/xap/xap.c | 10 +++--- quantum/xap/xap_handlers.c | 15 -------- 5 files changed, 43 insertions(+), 26 deletions(-) diff --git a/data/xap/xap_0.1.0.hjson b/data/xap/xap_0.1.0.hjson index 8fdbcc02a67c..38d36dddfae0 100755 --- a/data/xap/xap_0.1.0.hjson +++ b/data/xap/xap_0.1.0.hjson @@ -234,7 +234,7 @@ request_type: u16 request_purpose: offset return_type: u8[32] - return_execute: info_json_gz + return_execute: get_info_json_chunk } } }, diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index 13fc33de1a31..3eb107d78430 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -51,7 +51,7 @@ def print_dotted_output(kb_info_json, prefix=''): def _xap_transaction(device, sub, route, ret_len, *args): # gen token tok = random.getrandbits(16) - token = tok.to_bytes(2, byteorder='big') + token = tok.to_bytes(2, byteorder='little') # send with padding # TODO: this code is total garbage @@ -59,13 +59,13 @@ def _xap_transaction(device, sub, route, ret_len, *args): args_len = 2 if len(args) == 1: args_len += 2 - args_data = args[0].to_bytes(2, byteorder='big') + args_data = args[0].to_bytes(2, byteorder='little') padding_len = 64 - 3 - args_len padding = b"\x00" * padding_len if args_data: padding = args_data + padding - buffer = token + args_len.to_bytes(1, byteorder='big') + sub.to_bytes(1, byteorder='big') + route.to_bytes(1, byteorder='big') + padding + buffer = token + args_len.to_bytes(1, byteorder='little') + sub.to_bytes(1, byteorder='little') + route.to_bytes(1, byteorder='little') + padding # prepend 0 on windows because reasons... if 'windows' in platform().lower(): diff --git a/lib/python/qmk/xap/gen_firmware/inline_generator.py b/lib/python/qmk/xap/gen_firmware/inline_generator.py index 8563bfdc6905..914e31d0c4f4 100755 --- a/lib/python/qmk/xap/gen_firmware/inline_generator.py +++ b/lib/python/qmk/xap/gen_firmware/inline_generator.py @@ -24,6 +24,20 @@ def _get_c_type(xap_type): return 'unknown' +def _get_c_size(xap_type): + if xap_type == 'u8': + return 1 + elif xap_type == 'u16': + return 2 + elif xap_type == 'u32': + return 4 + elif xap_type == 'u64': + return 8 + elif xap_type == 'u8[32]': + return 32 + return -1 + + def _get_route_type(container): if 'routes' in container: return 'XAP_ROUTE' @@ -52,8 +66,26 @@ def _append_routing_table_declaration(lines, container, container_id, route_stac elif 'return_execute' in container: execute = container['return_execute'] - lines.append('') - lines.append(f'bool xap_respond_{execute}(xap_token_t token, const uint8_t *data, size_t data_len);') + request_type = container['request_type'] + return_type = container['return_type'] + lines.append(f''' +bool xap_respond_{execute}(xap_token_t token, const uint8_t *data, size_t data_len) {{ + if (data_len != sizeof({_get_c_type(request_type)})) {{ + xap_respond_failure(token, 0); + return false; + }} + + uint8_t ret[{_get_c_size(return_type)}] = {{0}}; + {_get_c_type(request_type)} *argp = ({_get_c_type(request_type)} *)&data[0]; + + bool {execute}({_get_c_type(request_type)} arg, uint8_t *ret, uint8_t ret_len); + if(!{execute}(*argp, ret, sizeof(ret))) {{ + xap_respond_failure(token, 0); + return false; + }} + + return xap_respond_data(token, ret, sizeof(ret)); +}}''') elif 'return_constant' in container: diff --git a/quantum/xap/xap.c b/quantum/xap/xap.c index f468eac9b414..fce5014c6235 100644 --- a/quantum/xap/xap.c +++ b/quantum/xap/xap.c @@ -18,13 +18,13 @@ #include #include "info_json_gz.h" -void get_info_json_chunk(uint8_t *data, size_t offset) { - uint8_t len = 32; - if (offset + len > info_json_gz_len) { - len = info_json_gz_len - offset; +bool get_info_json_chunk(uint16_t offset, uint8_t *data, uint8_t data_len) { + if (offset + data_len > info_json_gz_len) { + data_len = info_json_gz_len - offset; } - memcpy_P(data, &info_json_gz[offset], len); + memcpy_P(data, &info_json_gz[offset], data_len); + return true; } #define QSTR2(z) #z diff --git a/quantum/xap/xap_handlers.c b/quantum/xap/xap_handlers.c index f21cdab3160b..811e5439d476 100644 --- a/quantum/xap/xap_handlers.c +++ b/quantum/xap/xap_handlers.c @@ -40,18 +40,3 @@ bool xap_respond_u32(xap_token_t token, uint32_t value) { uint32_t xap_route_qmk_ffffffffffffffff_getter(void) { return 0x12345678; } - -bool xap_respond_info_json_gz(xap_token_t token, const uint8_t *data, size_t data_len) { - if (data_len != 2) { - xap_respond_failure(token, 0); - return false; - } - - uint8_t blob[32] = {0}; - uint16_t offset = ((uint16_t)data[0]) << 8 | data[1]; - - void get_info_json_chunk(uint8_t * data, size_t offset); - get_info_json_chunk(blob, offset); - - return xap_respond_data(token, blob, 32); -} From 73d2228524ff9efc774a6d15f78f571c3560d205 Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 30 Mar 2022 23:25:33 +0100 Subject: [PATCH 033/203] format --- lib/python/qmk/xap/gen_firmware/inline_generator.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/python/qmk/xap/gen_firmware/inline_generator.py b/lib/python/qmk/xap/gen_firmware/inline_generator.py index 914e31d0c4f4..9cee29b455b5 100755 --- a/lib/python/qmk/xap/gen_firmware/inline_generator.py +++ b/lib/python/qmk/xap/gen_firmware/inline_generator.py @@ -68,7 +68,8 @@ def _append_routing_table_declaration(lines, container, container_id, route_stac execute = container['return_execute'] request_type = container['request_type'] return_type = container['return_type'] - lines.append(f''' + lines.append( + f''' bool xap_respond_{execute}(xap_token_t token, const uint8_t *data, size_t data_len) {{ if (data_len != sizeof({_get_c_type(request_type)})) {{ xap_respond_failure(token, 0); @@ -85,7 +86,8 @@ def _append_routing_table_declaration(lines, container, container_id, route_stac }} return xap_respond_data(token, ret, sizeof(ret)); -}}''') +}}''' + ) elif 'return_constant' in container: From 646fdc7d170d76ccb6885434814191b2012683b8 Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 31 Mar 2022 01:18:26 +0100 Subject: [PATCH 034/203] Rework code gen for return of dynamic variables --- data/xap/xap_0.1.0.hjson | 16 ++++++++++++ lib/python/qmk/cli/xap/xap.py | 11 +++++--- .../qmk/xap/gen_firmware/inline_generator.py | 26 ++++++++++++++----- quantum/xap/xap.c | 23 +++++++++------- quantum/xap/xap.h | 1 + 5 files changed, 58 insertions(+), 19 deletions(-) diff --git a/data/xap/xap_0.1.0.hjson b/data/xap/xap_0.1.0.hjson index 38d36dddfae0..82dffb192b7d 100755 --- a/data/xap/xap_0.1.0.hjson +++ b/data/xap/xap_0.1.0.hjson @@ -127,6 +127,22 @@ 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 + ''' + return_type: u8 + return_value: secure_status + } } }, diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index 3eb107d78430..3626512829c7 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -82,7 +82,7 @@ def _xap_transaction(device, sub, route, ret_len, *args): return array_alpha[4:] -def _query_device_version(device): +def _query_device(device): ver_data = _xap_transaction(device, 0x00, 0x00, 4) if not ver_data: return {'xap': 'UNKNOWN'} @@ -91,7 +91,10 @@ def _query_device_version(device): a = (ver_data[3] << 24) + (ver_data[2] << 16) + (ver_data[1] << 8) + (ver_data[0]) ver = f'{a>>24}.{a>>16 & 0xFF}.{a & 0xFFFF}' - return {'xap': ver} + secure = int.from_bytes(_xap_transaction(device, 0x00, 0x03, 1), 'little') + secure = 'unlocked' if secure == 2 else 'LOCKED' + + return {'xap': ver, 'secure': secure} def _query_device_info_len(device): @@ -129,8 +132,8 @@ def _list_devices(): for dev in devices: device = hid.Device(path=dev['path']) - data = _query_device_version(device) - cli.log.info(" %04x:%04x %s %s [API:%s]", dev['vendor_id'], dev['product_id'], dev['manufacturer_string'], dev['product_string'], data['xap']) + data = _query_device(device) + cli.log.info(" %04x:%04x %s %s [API:%s] %s", dev['vendor_id'], dev['product_id'], dev['manufacturer_string'], dev['product_string'], data['xap'], data['secure']) if cli.config.general.verbose: # TODO: better formatting like "lsusb -v"? diff --git a/lib/python/qmk/xap/gen_firmware/inline_generator.py b/lib/python/qmk/xap/gen_firmware/inline_generator.py index 9cee29b455b5..93bb6c7afd70 100755 --- a/lib/python/qmk/xap/gen_firmware/inline_generator.py +++ b/lib/python/qmk/xap/gen_firmware/inline_generator.py @@ -43,9 +43,12 @@ def _get_route_type(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'] == 'u32': - return 'XAP_VALUE' + return 'XAP_CONST_MEM' elif container['return_type'] == 'struct': return 'XAP_CONST_MEM' elif container['return_type'] == 'string': @@ -89,10 +92,18 @@ def _append_routing_table_declaration(lines, container, container_id, route_stac }}''' ) + # 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'] == 'u32': - pass + 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('') @@ -146,9 +157,10 @@ def _append_routing_table_entry_execute(lines, container, container_id, route_st lines.append(f' .handler = xap_respond_{value},') -def _append_routing_table_entry_u32value(lines, container, container_id, route_stack): - value = container['return_constant'] - lines.append(f' .u32value = {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): @@ -183,9 +195,11 @@ def _append_routing_table_entry(lines, container, container_id, route_stack): _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'] == 'u32': - _append_routing_table_entry_u32value(lines, container, container_id, route_stack) + _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': diff --git a/quantum/xap/xap.c b/quantum/xap/xap.c index fce5014c6235..333e53e84581 100644 --- a/quantum/xap/xap.c +++ b/quantum/xap/xap.c @@ -27,6 +27,8 @@ bool get_info_json_chunk(uint16_t offset, uint8_t *data, uint8_t data_len) { return true; } +uint8_t secure_status = 2; + #define QSTR2(z) #z #define QSTR(z) QSTR2(z) @@ -63,15 +65,12 @@ struct __attribute__((packed)) xap_route_t { // XAP_EXECUTE bool (*handler)(xap_token_t token, const uint8_t *data, size_t data_len); - // XAP_VALUE - uint32_t u32value; - // XAP_GETTER uint32_t (*u32getter)(void); - // XAP_CONST_MEM + // XAP_VALUE / XAP_CONST_MEM struct { - const void * const_data; + const void *const_data; const uint8_t const_data_len; }; }; @@ -86,6 +85,12 @@ void xap_execute_route(xap_token_t token, const xap_route_t *routes, size_t max_ if (id < max_routes) { xap_route_t route; memcpy_P(&route, &routes[id], sizeof(xap_route_t)); + + if (route.flags.is_secure && secure_status != 2) { + xap_respond_failure(token, XAP_RESPONSE_FLAG_SECURE_FAILURE); + return; + } + switch (route.flags.type) { case XAP_ROUTE: if (route.child_routes != NULL && route.child_routes_len > 0 && data_len > 0) { @@ -101,14 +106,14 @@ void xap_execute_route(xap_token_t token, const xap_route_t *routes, size_t max_ } break; - case XAP_VALUE: - xap_respond_u32(token, route.u32value); - return; - case XAP_GETTER: xap_respond_u32(token, (route.u32getter)()); return; + 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; diff --git a/quantum/xap/xap.h b/quantum/xap/xap.h index 67e41d31ea1e..828531a92bb8 100644 --- a/quantum/xap/xap.h +++ b/quantum/xap/xap.h @@ -33,6 +33,7 @@ typedef uint16_t xap_token_t; #define XAP_RESPONSE_FLAG_FAILED 0 #define XAP_RESPONSE_FLAG_SUCCESS (1 << 0) +#define XAP_RESPONSE_FLAG_SECURE_FAILURE (1 << 1) 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); From fe1a4a52d421a4c9cbcd972e1aa284063dad485d Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 31 Mar 2022 01:23:19 +0100 Subject: [PATCH 035/203] clang --- quantum/xap/xap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quantum/xap/xap.c b/quantum/xap/xap.c index 333e53e84581..98a6c9902a6c 100644 --- a/quantum/xap/xap.c +++ b/quantum/xap/xap.c @@ -70,7 +70,7 @@ struct __attribute__((packed)) xap_route_t { // XAP_VALUE / XAP_CONST_MEM struct { - const void *const_data; + const void * const_data; const uint8_t const_data_len; }; }; From 0f5ced0521fb54095cc88249a8ffebf10ca17221 Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 31 Mar 2022 19:14:35 +0100 Subject: [PATCH 036/203] claim back a few bytes --- quantum/xap/xap.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/quantum/xap/xap.c b/quantum/xap/xap.c index 98a6c9902a6c..a5d8b3cc0d46 100644 --- a/quantum/xap/xap.c +++ b/quantum/xap/xap.c @@ -107,8 +107,12 @@ void xap_execute_route(xap_token_t token, const xap_route_t *routes, size_t max_ break; case XAP_GETTER: - xap_respond_u32(token, (route.u32getter)()); - return; + 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); From 81a53ac5b624c90c27310d1c6b9daabe1a8ad565 Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 31 Mar 2022 21:08:18 +0100 Subject: [PATCH 037/203] gen RESPONSE_FLAG defines --- data/xap/xap_0.0.1.hjson | 2 +- .../qmk/xap/gen_firmware/header_generator.py | 36 +++++++++++++++++++ quantum/xap/xap.h | 22 +----------- 3 files changed, 38 insertions(+), 22 deletions(-) diff --git a/data/xap/xap_0.0.1.hjson b/data/xap/xap_0.0.1.hjson index 9b19526244da..328207768e6c 100755 --- a/data/xap/xap_0.0.1.hjson +++ b/data/xap/xap_0.0.1.hjson @@ -132,7 +132,7 @@ } response_flags: { - define_prefix: XAP_RESP + define_prefix: XAP_RESPONSE_FLAG bits: { 0: { name: Success diff --git a/lib/python/qmk/xap/gen_firmware/header_generator.py b/lib/python/qmk/xap/gen_firmware/header_generator.py index 42d54e1960ce..6cc9ff8816e9 100755 --- a/lib/python/qmk/xap/gen_firmware/header_generator.py +++ b/lib/python/qmk/xap/gen_firmware/header_generator.py @@ -94,6 +94,38 @@ def _append_route_capabilities(lines, container, container_id=None, route_stack= route_stack.pop() +def _append_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') + + # TODO: define this in xap_*.hjson + lines.append( + ''' +typedef uint8_t xap_identifier_t; +typedef uint8_t xap_response_flags_t; +typedef uint16_t xap_token_t; + +typedef struct { + xap_token_t token; + uint8_t length; +} xap_request_header_t; + +typedef struct { + xap_token_t token; + xap_response_flags_t flags; + uint8_t length; +} xap_response_header_t;''' + ) + + def generate_header(output_file, keyboard): """Generates the XAP protocol header file, generated during normal build. """ @@ -112,6 +144,10 @@ def generate_header(output_file, keyboard): lines.append(f'#define XAP_KEYBOARD_IDENTIFIER 0x{keyboard_id:08X}ul') lines.append('') + # Types + _append_types(lines, xap_defs) + lines.append('') + # Append the route and command defines _append_route_defines(lines, xap_defs) lines.append('') diff --git a/quantum/xap/xap.h b/quantum/xap/xap.h index 828531a92bb8..7c43d90e278f 100644 --- a/quantum/xap/xap.h +++ b/quantum/xap/xap.h @@ -19,9 +19,7 @@ #include #include -typedef uint8_t xap_identifier_t; -typedef uint8_t xap_response_flags_t; -typedef uint16_t xap_token_t; +#include #ifndef XAP_SUBSYSTEM_VERSION_KB # define XAP_SUBSYSTEM_VERSION_KB 0 @@ -31,27 +29,9 @@ typedef uint16_t xap_token_t; # define XAP_SUBSYSTEM_VERSION_USER 0 #endif -#define XAP_RESPONSE_FLAG_FAILED 0 -#define XAP_RESPONSE_FLAG_SUCCESS (1 << 0) -#define XAP_RESPONSE_FLAG_SECURE_FAILURE (1 << 1) - 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); - -// TODO: gen from xap defs? -typedef struct { - xap_token_t token; - uint8_t length; -} xap_request_header_t; - -typedef struct { - xap_token_t token; - xap_response_flags_t flags; - uint8_t length; -} xap_response_header_t; - -#include From ffcdfc6c032578e89b7c409c8afb15f1b907fd1e Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Fri, 1 Apr 2022 08:15:15 +1100 Subject: [PATCH 038/203] Swap info.json.gz length to #define. --- data/xap/xap_0.1.0.hjson | 2 +- lib/python/qmk/xap/gen_firmware/info_generator.py | 2 +- quantum/xap/xap.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/data/xap/xap_0.1.0.hjson b/data/xap/xap_0.1.0.hjson index 82dffb192b7d..42b0476f7a9c 100755 --- a/data/xap/xap_0.1.0.hjson +++ b/data/xap/xap_0.1.0.hjson @@ -240,7 +240,7 @@ define: INFO_LEN_QUERY description: Retrieves the length of info.json return_type: u32 - return_constant: info_json_gz_len + return_constant: INFO_JSON_GZ_LEN } 0x06: { type: command diff --git a/lib/python/qmk/xap/gen_firmware/info_generator.py b/lib/python/qmk/xap/gen_firmware/info_generator.py index c25d82181b9c..a1a6f1b40c61 100644 --- a/lib/python/qmk/xap/gen_firmware/info_generator.py +++ b/lib/python/qmk/xap/gen_firmware/info_generator.py @@ -36,6 +36,6 @@ def generate_info(output_file, keyboard, keymap): lines.append('static const unsigned char info_json_gz[] PROGMEM = {') lines.append(data) lines.append('};') - lines.append(f'static const unsigned int info_json_gz_len = {data_len};') + lines.append(f'#define INFO_JSON_GZ_LEN {data_len}') dump_lines(output_file, lines) diff --git a/quantum/xap/xap.c b/quantum/xap/xap.c index a5d8b3cc0d46..00901b0423e8 100644 --- a/quantum/xap/xap.c +++ b/quantum/xap/xap.c @@ -19,8 +19,8 @@ #include "info_json_gz.h" bool get_info_json_chunk(uint16_t offset, uint8_t *data, uint8_t data_len) { - if (offset + data_len > info_json_gz_len) { - data_len = info_json_gz_len - offset; + if (offset + data_len > INFO_JSON_GZ_LEN) { + data_len = INFO_JSON_GZ_LEN - offset; } memcpy_P(data, &info_json_gz[offset], data_len); From 53052228df2e0cbf1154878679f7f06f93ea37c7 Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 31 Mar 2022 22:34:01 +0100 Subject: [PATCH 039/203] Add types codegen --- data/xap/xap_0.0.1.hjson | 68 +++++++++++++++---- .../qmk/xap/gen_firmware/header_generator.py | 58 +++++++++++----- 2 files changed, 96 insertions(+), 30 deletions(-) diff --git a/data/xap/xap_0.0.1.hjson b/data/xap/xap_0.0.1.hjson index 328207768e6c..31aa5fdafcc0 100755 --- a/data/xap/xap_0.0.1.hjson +++ b/data/xap/xap_0.0.1.hjson @@ -101,10 +101,6 @@ ''' A high-level area of functionality within XAP. ''' - ID: - ''' - A single octet / 8-bit byte. - ''' Route: ''' A sequence of _IDs_ describing the route to invoke a _handler_. @@ -113,24 +109,72 @@ ''' A piece of code that is executed when a specific _route_ is received. ''' - Token: - ''' - A `u16` associated with a specific request as well as its corresponding response. - ''' Response: ''' The data sent back to the host during execution of a _handler_. ''' - "Response Flags": - ''' - An `u8` containing the status of the request. - ''' Payload: ''' Any received data appended to the _route_, which gets delivered to the _handler_ when received. ''' } + types: { + identifier: { + name: ID + description: Subsystem/Route ID + type: u8 + } + + response_flags: { + name: Response Flags + description: Contains the status of the request + type: u8 + } + + token: { + name: Token + description: ID associated with a specific request as well as its corresponding response + type: u16 + } + + request_header: { + name: Request Header + description: asdf + type: struct + struct_members: [ + { + type: token + name: token + }, + { + type: u8 + name: length + } + ] + } + + response_header: { + name: Response Header + description: asdf + type: struct + struct_members: [ + { + type: token + name: token + }, + { + type: response_flags + name: flags + }, + { + type: u8 + name: length + } + ] + } + } + response_flags: { define_prefix: XAP_RESPONSE_FLAG bits: { diff --git a/lib/python/qmk/xap/gen_firmware/header_generator.py b/lib/python/qmk/xap/gen_firmware/header_generator.py index 6cc9ff8816e9..e8e2465e4dcf 100755 --- a/lib/python/qmk/xap/gen_firmware/header_generator.py +++ b/lib/python/qmk/xap/gen_firmware/header_generator.py @@ -9,6 +9,24 @@ from qmk.xap.common import latest_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. """ @@ -105,25 +123,29 @@ def _append_types(lines, container): # Add special lines.append(f'#define {prefix}_FAILED 0x00') + lines.append('') - # TODO: define this in xap_*.hjson - lines.append( - ''' -typedef uint8_t xap_identifier_t; -typedef uint8_t xap_response_flags_t; -typedef uint16_t xap_token_t; - -typedef struct { - xap_token_t token; - uint8_t length; -} xap_request_header_t; - -typedef struct { - xap_token_t token; - xap_response_flags_t flags; - uint8_t length; -} xap_response_header_t;''' - ) + additional_types = {} + types = container.get('types', {}) + 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'}} xap_{key}_t;') + else: + lines.append(f'typedef {data_type} xap_{key}_t;') def generate_header(output_file, keyboard): From 2c8c9c9928b4880bc91884451cbbc4b9afc6d705 Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 31 Mar 2022 23:16:56 +0100 Subject: [PATCH 040/203] And sort out docs gen too --- data/templates/xap/docs/term_definitions.md.j2 | 3 +++ data/xap/xap_0.0.1.hjson | 10 +++++----- docs/xap_0.0.1.md | 6 ++++-- docs/xap_0.1.0.md | 8 +++++--- docs/xap_0.2.0.md | 8 +++++--- 5 files changed, 22 insertions(+), 13 deletions(-) diff --git a/data/templates/xap/docs/term_definitions.md.j2 b/data/templates/xap/docs/term_definitions.md.j2 index 8afdb9c3e215..39587edb49f5 100644 --- a/data/templates/xap/docs/term_definitions.md.j2 +++ b/data/templates/xap/docs/term_definitions.md.j2 @@ -2,4 +2,7 @@ | -- | -- | {%- for type, definition in xap.term_definitions | dictsort %} | _{{ type }}_ | {{ definition }} | +{%- endfor %} +{%- for type, definition in xap.types | dictsort %} +| _{{ definition.name }}_ | {{ definition.description }}{% if 'struct' == definition.type %} Takes the format:{% for item in definition.struct_members %}
`{{ item.type }}` - {{ item.name }}{%- endfor %}{% endif %} | {%- endfor %} \ No newline at end of file diff --git a/data/xap/xap_0.0.1.hjson b/data/xap/xap_0.0.1.hjson index 31aa5fdafcc0..a2abcb5fd62a 100755 --- a/data/xap/xap_0.0.1.hjson +++ b/data/xap/xap_0.0.1.hjson @@ -122,25 +122,25 @@ types: { identifier: { name: ID - description: Subsystem/Route ID + description: A single octet / 8-bit byte, representing Subsystem or Route index. type: u8 } response_flags: { name: Response Flags - description: Contains the status of the request + description: An `u8` containing the status of the request. type: u8 } token: { name: Token - description: ID associated with a specific request as well as its corresponding response + description: A `u16` associated with a specific request as well as its corresponding response. type: u16 } request_header: { name: Request Header - description: asdf + description: Packet format for inbound data. type: struct struct_members: [ { @@ -156,7 +156,7 @@ response_header: { name: Response Header - description: asdf + description: Packet format for inbound data. type: struct struct_members: [ { diff --git a/docs/xap_0.0.1.md b/docs/xap_0.0.1.md index 336176c33c14..afa23c85c367 100644 --- a/docs/xap_0.0.1.md +++ b/docs/xap_0.0.1.md @@ -20,12 +20,14 @@ This list defines the terms used across the entire set of XAP protocol documenta | Name | Definition | | -- | -- | | _Handler_ | A piece of code that is executed when a specific _route_ is received. | -| _ID_ | A single octet / 8-bit byte. | | _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_. | -| _Response Flags_ | An `u8` containing the status of the request. | | _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:
`token` - token
`u8` - length | +| _Response Flags_ | An `u8` containing the status of the request. | +| _Response Header_ | Packet format for inbound data. Takes the format:
`token` - token
`response_flags` - flags
`u8` - length | | _Token_ | A `u16` associated with a specific request as well as its corresponding response. | ## Requests and Responses diff --git a/docs/xap_0.1.0.md b/docs/xap_0.1.0.md index f3911d7220d5..c61ee1df6037 100644 --- a/docs/xap_0.1.0.md +++ b/docs/xap_0.1.0.md @@ -23,15 +23,17 @@ This list defines the terms used across the entire set of XAP protocol documenta | -- | -- | | _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. | -| _ID_ | A single octet / 8-bit byte. | | _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_. | -| _Response Flags_ | An `u8` containing the status of the request. | | _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. | -| _Token_ | A `u16` associated with a specific request as well as its corresponding response. | | _Unlock sequence_ | A physical sequence initiated by the user to enable execution of _secure routes_. | +| _ID_ | A single octet / 8-bit byte, representing Subsystem or Route index. | +| _Request Header_ | Packet format for inbound data. Takes the format:
`token` - token
`u8` - length | +| _Response Flags_ | An `u8` containing the status of the request. | +| _Response Header_ | Packet format for inbound data. Takes the format:
`token` - token
`response_flags` - flags
`u8` - length | +| _Token_ | A `u16` associated with a specific request as well as its corresponding response. | ## Requests and Responses diff --git a/docs/xap_0.2.0.md b/docs/xap_0.2.0.md index f3911d7220d5..c61ee1df6037 100644 --- a/docs/xap_0.2.0.md +++ b/docs/xap_0.2.0.md @@ -23,15 +23,17 @@ This list defines the terms used across the entire set of XAP protocol documenta | -- | -- | | _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. | -| _ID_ | A single octet / 8-bit byte. | | _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_. | -| _Response Flags_ | An `u8` containing the status of the request. | | _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. | -| _Token_ | A `u16` associated with a specific request as well as its corresponding response. | | _Unlock sequence_ | A physical sequence initiated by the user to enable execution of _secure routes_. | +| _ID_ | A single octet / 8-bit byte, representing Subsystem or Route index. | +| _Request Header_ | Packet format for inbound data. Takes the format:
`token` - token
`u8` - length | +| _Response Flags_ | An `u8` containing the status of the request. | +| _Response Header_ | Packet format for inbound data. Takes the format:
`token` - token
`response_flags` - flags
`u8` - length | +| _Token_ | A `u16` associated with a specific request as well as its corresponding response. | ## Requests and Responses From e111b9d01743c4e6c8f6772f0a71e7127dd344cf Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 31 Mar 2022 23:31:01 +0100 Subject: [PATCH 041/203] Use slightly more unique data name --- data/templates/xap/docs/term_definitions.md.j2 | 2 +- data/xap/xap_0.0.1.hjson | 2 +- lib/python/qmk/xap/gen_firmware/header_generator.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/data/templates/xap/docs/term_definitions.md.j2 b/data/templates/xap/docs/term_definitions.md.j2 index 39587edb49f5..9b2863eaa5f4 100644 --- a/data/templates/xap/docs/term_definitions.md.j2 +++ b/data/templates/xap/docs/term_definitions.md.j2 @@ -3,6 +3,6 @@ {%- for type, definition in xap.term_definitions | dictsort %} | _{{ type }}_ | {{ definition }} | {%- endfor %} -{%- for type, definition in xap.types | dictsort %} +{%- 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 %}
`{{ item.type }}` - {{ item.name }}{%- endfor %}{% endif %} | {%- endfor %} \ No newline at end of file diff --git a/data/xap/xap_0.0.1.hjson b/data/xap/xap_0.0.1.hjson index a2abcb5fd62a..b8a479f01b9d 100755 --- a/data/xap/xap_0.0.1.hjson +++ b/data/xap/xap_0.0.1.hjson @@ -119,7 +119,7 @@ ''' } - types: { + type_definitions: { identifier: { name: ID description: A single octet / 8-bit byte, representing Subsystem or Route index. diff --git a/lib/python/qmk/xap/gen_firmware/header_generator.py b/lib/python/qmk/xap/gen_firmware/header_generator.py index e8e2465e4dcf..a4b7d7af95fe 100755 --- a/lib/python/qmk/xap/gen_firmware/header_generator.py +++ b/lib/python/qmk/xap/gen_firmware/header_generator.py @@ -126,7 +126,7 @@ def _append_types(lines, container): lines.append('') additional_types = {} - types = container.get('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' From c5842ab9b503ea65729c3d3c3cb547fbfa288d33 Mon Sep 17 00:00:00 2001 From: zvecr Date: Fri, 1 Apr 2022 00:44:27 +0100 Subject: [PATCH 042/203] stub out return_execute with zero args --- data/xap/xap_0.1.0.hjson | 18 +++++++++++ lib/python/qmk/cli/xap/xap.py | 1 + .../qmk/xap/gen_firmware/inline_generator.py | 32 +++++++++++++------ quantum/xap/xap.c | 15 +++++++++ 4 files changed, 57 insertions(+), 9 deletions(-) diff --git a/data/xap/xap_0.1.0.hjson b/data/xap/xap_0.1.0.hjson index 42b0476f7a9c..132fc421bc35 100755 --- a/data/xap/xap_0.1.0.hjson +++ b/data/xap/xap_0.1.0.hjson @@ -252,6 +252,24 @@ return_type: u8[32] return_execute: get_info_json_chunk } + 0x07: { + type: command + name: Jump to bootloader + define: BOOTLOADER_JUMP + secure: true + 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 + } } }, diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index 3626512829c7..1ba144a644e4 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -139,6 +139,7 @@ def _list_devices(): # TODO: better formatting like "lsusb -v"? data = _query_device_info(device) print_dotted_output(data) + # _xap_transaction(device, 0x01, 0x07, 1) @cli.argument('-d', '--device', help='device to select - uses format :.') diff --git a/lib/python/qmk/xap/gen_firmware/inline_generator.py b/lib/python/qmk/xap/gen_firmware/inline_generator.py index 93bb6c7afd70..2ae2221c7bc1 100755 --- a/lib/python/qmk/xap/gen_firmware/inline_generator.py +++ b/lib/python/qmk/xap/gen_firmware/inline_generator.py @@ -26,16 +26,16 @@ def _get_c_type(xap_type): def _get_c_size(xap_type): if xap_type == 'u8': - return 1 + return 'sizeof(uint8_t)' elif xap_type == 'u16': - return 2 + return 'sizeof(uint16_t)' elif xap_type == 'u32': - return 4 + return 'sizeof(uint32_t)' elif xap_type == 'u64': return 8 elif xap_type == 'u8[32]': return 32 - return -1 + return 0 def _get_route_type(container): @@ -69,17 +69,30 @@ def _append_routing_table_declaration(lines, container, container_id, route_stac elif 'return_execute' in container: execute = container['return_execute'] - request_type = container['request_type'] + request_type = container.get('request_type', None) return_type = container['return_type'] lines.append( f''' bool xap_respond_{execute}(xap_token_t token, const uint8_t *data, size_t data_len) {{ - if (data_len != sizeof({_get_c_type(request_type)})) {{ + if (data_len != {_get_c_size(request_type)}) {{ xap_respond_failure(token, 0); return false; }} uint8_t ret[{_get_c_size(return_type)}] = {{0}}; +''' + ) + if not request_type: + lines.append(f''' + bool {execute}(uint8_t *ret, uint8_t ret_len); + if(!{execute}(ret, sizeof(ret))) {{ + xap_respond_failure(token, 0); + return false; + }} +''') + else: + lines.append( + f''' {_get_c_type(request_type)} *argp = ({_get_c_type(request_type)} *)&data[0]; bool {execute}({_get_c_type(request_type)} arg, uint8_t *ret, uint8_t ret_len); @@ -87,10 +100,11 @@ def _append_routing_table_declaration(lines, container, container_id, route_stac xap_respond_failure(token, 0); return false; }} - +''' + ) + lines.append(''' return xap_respond_data(token, ret, sizeof(ret)); -}}''' - ) +}''') # elif 'return_value' in container: # value = container['return_value'] diff --git a/quantum/xap/xap.c b/quantum/xap/xap.c index 00901b0423e8..1eb3786cc179 100644 --- a/quantum/xap/xap.c +++ b/quantum/xap/xap.c @@ -29,6 +29,21 @@ bool get_info_json_chunk(uint16_t offset, uint8_t *data, uint8_t data_len) { uint8_t secure_status = 2; +// TODO: how to set this if "custom" is just an empty stub +#ifndef BOOTLOADER_JUMP_SUPPORTED +# define BOOTLOADER_JUMP_SUPPORTED +#endif + +#ifdef BOOTLOADER_JUMP_SUPPORTED +bool request_bootloader_jump(uint8_t *data, uint8_t data_len) { + data[0] = secure_status == 2; + + // TODO: post to deferred queue so this request can return? + reset_keyboard(); + return true; +} +#endif + #define QSTR2(z) #z #define QSTR(z) QSTR2(z) From e7d9d6675cec0ebca5c40700875f2de2db726d56 Mon Sep 17 00:00:00 2001 From: zvecr Date: Fri, 1 Apr 2022 23:37:58 +0100 Subject: [PATCH 043/203] Implement codegen for more data types - codegen for return_execute stubs removed --- data/xap/xap_0.2.0.hjson | 58 +++++++++++++++ lib/python/qmk/cli/xap/xap.py | 29 +++++++- .../qmk/xap/gen_firmware/header_generator.py | 58 ++++++++++++++- .../qmk/xap/gen_firmware/inline_generator.py | 70 ++++++------------- quantum/xap/xap.c | 15 ---- quantum/xap/xap_handlers.c | 60 ++++++++++++++++ 6 files changed, 223 insertions(+), 67 deletions(-) diff --git a/data/xap/xap_0.2.0.hjson b/data/xap/xap_0.2.0.hjson index 81c518739046..f2fce07b844d 100755 --- a/data/xap/xap_0.2.0.hjson +++ b/data/xap/xap_0.2.0.hjson @@ -24,6 +24,64 @@ return_purpose: capabilities return_constant: XAP_ROUTE_DYNAMIC_KEYMAP_CAPABILITIES } + 0x01: { + type: command + name: Get Layer Count + define: GET_LAYER_COUNT + description: TODO + return_type: u8 + return_constant: DYNAMIC_KEYMAP_LAYER_COUNT + } + 0x02: { + type: command + name: Get Keycode + define: GET_KEYMAP_KEYCODE + description: TODO + request_type: struct + request_struct_members: [ + { + type: u8 + name: Layer + }, + { + type: u8 + name: Row + }, + { + type: u8 + name: Column + } + ] + return_type: u16 + return_execute: dynamic_keymap_get_keycode + } + 0x03: { + type: command + name: Set Keycode + define: SET_KEYMAP_KEYCODE + description: TODO + request_type: struct + request_struct_members: [ + { + type: u8 + name: Layer + }, + { + type: u8 + name: Row + }, + { + type: u8 + name: Column + }, + { + type: u16 + name: Keycode + } + ] + return_type: u8 + return_execute: dynamic_keymap_set_keycode + } } } diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index 1ba144a644e4..fa24a23edec7 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -58,8 +58,12 @@ def _xap_transaction(device, sub, route, ret_len, *args): args_data = [] args_len = 2 if len(args) == 1: - args_len += 2 - args_data = args[0].to_bytes(2, byteorder='little') + if isinstance(args[0], (bytes, bytearray)): + args_len += len(args[0]) + args_data = args[0] + else: + args_len += 2 + args_data = args[0].to_bytes(2, byteorder='little') padding_len = 64 - 3 - args_len padding = b"\x00" * padding_len @@ -156,4 +160,23 @@ def xap(cli): if cli.args.list: return _list_devices() - cli.log.warn("TODO: Device specific stuff") + # Connect to first available device + dev = _search()[0] + device = hid.Device(path=dev['path']) + cli.log.info("Connected to:%04x:%04x %s %s", dev['vendor_id'], dev['product_id'], dev['manufacturer_string'], dev['product_string']) + + # get layer count + layers = _xap_transaction(device, 0x04, 0x01, 1) + layers = int.from_bytes(layers, "little") + print(f'layers:{layers}') + + # get keycode [layer:0, row:0, col:0] + keycode = _xap_transaction(device, 0x04, 0x02, 2, b"\x00\x00\x00") + keycode = int.from_bytes(keycode, "little") + keycode_map = { + 0x29: 'KC_ESCAPE' + } + print('keycode:' + keycode_map.get(keycode, 'unknown')) + + # Reboot + # _xap_transaction(device, 0x01, 0x07, 1) diff --git a/lib/python/qmk/xap/gen_firmware/header_generator.py b/lib/python/qmk/xap/gen_firmware/header_generator.py index a4b7d7af95fe..3abfeaf4e6b1 100755 --- a/lib/python/qmk/xap/gen_firmware/header_generator.py +++ b/lib/python/qmk/xap/gen_firmware/header_generator.py @@ -3,6 +3,7 @@ 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 @@ -112,7 +113,58 @@ def _append_route_capabilities(lines, container, container_id=None, route_stack= route_stack.pop() -def _append_types(lines, container): +def _append_route_types(lines, container, container_id=None, route_stack=None): + """Handles creating + """ + 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(f'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'}} {route_name}_arg_t;') + + elif 'request_type' in container: + request_type = container['request_type'] + 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(f'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'}} {route_name}_t;') + + elif 'return_type' in container: + return_type = container['return_type'] + if return_type == 'u8[32]': + lines.append(f'typedef struct {{ uint8_t x[32]; }} {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', {}) @@ -167,7 +219,9 @@ def generate_header(output_file, keyboard): lines.append('') # Types - _append_types(lines, xap_defs) + _append_internal_types(lines, xap_defs) + lines.append('') + _append_route_types(lines, xap_defs) lines.append('') # Append the route and command defines diff --git a/lib/python/qmk/xap/gen_firmware/inline_generator.py b/lib/python/qmk/xap/gen_firmware/inline_generator.py index 2ae2221c7bc1..5b8117620294 100755 --- a/lib/python/qmk/xap/gen_firmware/inline_generator.py +++ b/lib/python/qmk/xap/gen_firmware/inline_generator.py @@ -47,7 +47,11 @@ def _get_route_type(container): if container['return_type'] == 'u8': return 'XAP_VALUE' elif 'return_constant' in container: - if container['return_type'] == 'u32': + 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' @@ -69,42 +73,7 @@ def _append_routing_table_declaration(lines, container, container_id, route_stac elif 'return_execute' in container: execute = container['return_execute'] - request_type = container.get('request_type', None) - return_type = container['return_type'] - lines.append( - f''' -bool xap_respond_{execute}(xap_token_t token, const uint8_t *data, size_t data_len) {{ - if (data_len != {_get_c_size(request_type)}) {{ - xap_respond_failure(token, 0); - return false; - }} - - uint8_t ret[{_get_c_size(return_type)}] = {{0}}; -''' - ) - if not request_type: - lines.append(f''' - bool {execute}(uint8_t *ret, uint8_t ret_len); - if(!{execute}(ret, sizeof(ret))) {{ - xap_respond_failure(token, 0); - return false; - }} -''') - else: - lines.append( - f''' - {_get_c_type(request_type)} *argp = ({_get_c_type(request_type)} *)&data[0]; - - bool {execute}({_get_c_type(request_type)} arg, uint8_t *ret, uint8_t ret_len); - if(!{execute}(*argp, ret, sizeof(ret))) {{ - xap_respond_failure(token, 0); - return false; - }} -''' - ) - lines.append(''' - return xap_respond_data(token, ret, sizeof(ret)); -}''') + 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'] @@ -114,21 +83,24 @@ def _append_routing_table_declaration(lines, container, container_id, route_stac elif 'return_constant' in container: - if container['return_type'] == 'u32': + if container['return_type'] == 'u8': constant = container['return_constant'] lines.append('') - lines.append(f'static const uint32_t {route_name}_data PROGMEM = {constant};') + lines.append(f'static const uint8_t {route_name}_data PROGMEM = {constant};') - elif container['return_type'] == 'struct': + elif container['return_type'] == 'u16': + constant = container['return_constant'] lines.append('') - lines.append(f'static const struct {route_name}_t {{') + lines.append(f'static const uint16_t {route_name}_data PROGMEM = {constant};') - for member in container['return_struct_members']: - member_type = _get_c_type(member['type']) - member_name = to_snake(member['name']) - lines.append(f' const {member_type} {member_name};') + elif container['return_type'] == 'u32': + constant = container['return_constant'] + lines.append('') + lines.append(f'static const uint32_t {route_name}_data PROGMEM = {constant};') - lines.append(f'}} {route_name}_data PROGMEM = {{') + 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},') @@ -212,7 +184,11 @@ def _append_routing_table_entry(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'] == 'u32': + 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) diff --git a/quantum/xap/xap.c b/quantum/xap/xap.c index 1eb3786cc179..00901b0423e8 100644 --- a/quantum/xap/xap.c +++ b/quantum/xap/xap.c @@ -29,21 +29,6 @@ bool get_info_json_chunk(uint16_t offset, uint8_t *data, uint8_t data_len) { uint8_t secure_status = 2; -// TODO: how to set this if "custom" is just an empty stub -#ifndef BOOTLOADER_JUMP_SUPPORTED -# define BOOTLOADER_JUMP_SUPPORTED -#endif - -#ifdef BOOTLOADER_JUMP_SUPPORTED -bool request_bootloader_jump(uint8_t *data, uint8_t data_len) { - data[0] = secure_status == 2; - - // TODO: post to deferred queue so this request can return? - reset_keyboard(); - return true; -} -#endif - #define QSTR2(z) #z #define QSTR(z) QSTR2(z) diff --git a/quantum/xap/xap_handlers.c b/quantum/xap/xap_handlers.c index 811e5439d476..adee30862fa9 100644 --- a/quantum/xap/xap_handlers.c +++ b/quantum/xap/xap_handlers.c @@ -17,6 +17,10 @@ #include #include +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); } @@ -40,3 +44,59 @@ bool xap_respond_u32(xap_token_t token, uint32_t value) { uint32_t xap_route_qmk_ffffffffffffffff_getter(void) { return 0x12345678; } + +bool xap_respond_get_info_json_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_info_query_t ret = {0}; + + bool get_info_json_chunk(uint16_t offset, uint8_t *data, uint8_t data_len); + get_info_json_chunk(offset, (uint8_t *)&ret, sizeof(ret)); + + return xap_respond_data(token, &ret, sizeof(ret)); +} + +// TODO: how to set this if "custom" is just an empty stub +#ifndef BOOTLOADER_JUMP_SUPPORTED +# define BOOTLOADER_JUMP_SUPPORTED +#endif + +#ifdef BOOTLOADER_JUMP_SUPPORTED +bool xap_respond_request_bootloader_jump(xap_token_t token, const void *data, size_t length) { + extern uint8_t secure_status; + uint8_t ret = secure_status == 2; + + // TODO: post to deferred queue so this request can return? + bool res = xap_respond_data(token, &ret, sizeof(ret)); + reset_keyboard(); + return res; +} +#endif + +#if ((defined(DYNAMIC_KEYMAP_ENABLE))) +bool xap_respond_dynamic_keymap_get_keycode(xap_token_t token, const void *data, size_t length) { + if(length != sizeof(xap_route_dynamic_keymap_get_keymap_keycode_arg_t)){ + return false; + } + + xap_route_dynamic_keymap_get_keymap_keycode_arg_t* arg = (xap_route_dynamic_keymap_get_keymap_keycode_arg_t*)data; + + uint16_t keycode = dynamic_keymap_get_keycode(arg->layer, arg->row, arg->column); + return xap_respond_data(token, &keycode, sizeof(keycode)); +} + +bool xap_respond_dynamic_keymap_set_keycode(xap_token_t token, const void *data, size_t length) { + if(length != sizeof(xap_route_dynamic_keymap_set_keymap_keycode_arg_t)){ + return false; + } + + xap_route_dynamic_keymap_set_keymap_keycode_arg_t* arg = (xap_route_dynamic_keymap_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 From c9eae1d3842f43855dd763996d2a44becfc18f42 Mon Sep 17 00:00:00 2001 From: zvecr Date: Sat, 2 Apr 2022 00:11:22 +0100 Subject: [PATCH 044/203] format --- lib/python/qmk/cli/xap/xap.py | 3 +++ .../qmk/xap/gen_firmware/header_generator.py | 6 +++--- quantum/xap/xap_handlers.c | 18 +++++++++--------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index fa24a23edec7..5765bfeb8ab7 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -174,6 +174,9 @@ def xap(cli): keycode = _xap_transaction(device, 0x04, 0x02, 2, b"\x00\x00\x00") keycode = int.from_bytes(keycode, "little") keycode_map = { + # TODO: this should be data driven... + 0x04: 'KC_A', + 0x05: 'KC_B', 0x29: 'KC_ESCAPE' } print('keycode:' + keycode_map.get(keycode, 'unknown')) diff --git a/lib/python/qmk/xap/gen_firmware/header_generator.py b/lib/python/qmk/xap/gen_firmware/header_generator.py index 3abfeaf4e6b1..6b589474715d 100755 --- a/lib/python/qmk/xap/gen_firmware/header_generator.py +++ b/lib/python/qmk/xap/gen_firmware/header_generator.py @@ -114,7 +114,7 @@ def _append_route_capabilities(lines, container, container_id=None, route_stack= def _append_route_types(lines, container, container_id=None, route_stack=None): - """Handles creating + """Handles creating typedefs used by routes """ if route_stack is None: route_stack = [container] @@ -126,7 +126,7 @@ def _append_route_types(lines, container, container_id=None, route_stack=None): # Inbound if 'request_struct_members' in container: request_struct_members = container['request_struct_members'] - lines.append(f'typedef struct {{') + lines.append('typedef struct {') for member in request_struct_members: member_type = _get_c_type(member['type']) member_name = to_snake(member['name']) @@ -141,7 +141,7 @@ def _append_route_types(lines, container, container_id=None, route_stack=None): qualifier = 'const' if 'return_constant' in container else '' if 'return_struct_members' in container: return_struct_members = container['return_struct_members'] - lines.append(f'typedef struct {{') + 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"])}' diff --git a/quantum/xap/xap_handlers.c b/quantum/xap/xap_handlers.c index adee30862fa9..70cd2fb2726b 100644 --- a/quantum/xap/xap_handlers.c +++ b/quantum/xap/xap_handlers.c @@ -46,14 +46,14 @@ uint32_t xap_route_qmk_ffffffffffffffff_getter(void) { } bool xap_respond_get_info_json_chunk(xap_token_t token, const void *data, size_t length) { - if(length != sizeof(uint16_t)){ + if (length != sizeof(uint16_t)) { return false; } - uint16_t offset = *((uint16_t*)data); - xap_route_qmk_info_query_t ret = {0}; + uint16_t offset = *((uint16_t *)data); + xap_route_qmk_info_query_t ret = {0}; - bool get_info_json_chunk(uint16_t offset, uint8_t *data, uint8_t data_len); + bool get_info_json_chunk(uint16_t offset, uint8_t * data, uint8_t data_len); get_info_json_chunk(offset, (uint8_t *)&ret, sizeof(ret)); return xap_respond_data(token, &ret, sizeof(ret)); @@ -67,7 +67,7 @@ bool xap_respond_get_info_json_chunk(xap_token_t token, const void *data, size_t #ifdef BOOTLOADER_JUMP_SUPPORTED bool xap_respond_request_bootloader_jump(xap_token_t token, const void *data, size_t length) { extern uint8_t secure_status; - uint8_t ret = secure_status == 2; + uint8_t ret = secure_status == 2; // TODO: post to deferred queue so this request can return? bool res = xap_respond_data(token, &ret, sizeof(ret)); @@ -78,22 +78,22 @@ bool xap_respond_request_bootloader_jump(xap_token_t token, const void *data, si #if ((defined(DYNAMIC_KEYMAP_ENABLE))) bool xap_respond_dynamic_keymap_get_keycode(xap_token_t token, const void *data, size_t length) { - if(length != sizeof(xap_route_dynamic_keymap_get_keymap_keycode_arg_t)){ + if (length != sizeof(xap_route_dynamic_keymap_get_keymap_keycode_arg_t)) { return false; } - xap_route_dynamic_keymap_get_keymap_keycode_arg_t* arg = (xap_route_dynamic_keymap_get_keymap_keycode_arg_t*)data; + xap_route_dynamic_keymap_get_keymap_keycode_arg_t *arg = (xap_route_dynamic_keymap_get_keymap_keycode_arg_t *)data; uint16_t keycode = dynamic_keymap_get_keycode(arg->layer, arg->row, arg->column); return xap_respond_data(token, &keycode, sizeof(keycode)); } bool xap_respond_dynamic_keymap_set_keycode(xap_token_t token, const void *data, size_t length) { - if(length != sizeof(xap_route_dynamic_keymap_set_keymap_keycode_arg_t)){ + if (length != sizeof(xap_route_dynamic_keymap_set_keymap_keycode_arg_t)) { return false; } - xap_route_dynamic_keymap_set_keymap_keycode_arg_t* arg = (xap_route_dynamic_keymap_set_keymap_keycode_arg_t*)data; + xap_route_dynamic_keymap_set_keymap_keycode_arg_t *arg = (xap_route_dynamic_keymap_set_keymap_keycode_arg_t *)data; dynamic_keymap_set_keycode(arg->layer, arg->row, arg->column, arg->keycode); xap_respond_success(token); From c1b57354f61e1e1456b46d448108a8ebeaaf49ae Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 5 Apr 2022 18:54:28 +0100 Subject: [PATCH 045/203] Stub out more of broadcast messages --- data/xap/xap_0.1.0.hjson | 31 +++++++ lib/python/qmk/cli/xap/xap.py | 82 +++++++++++++------ .../qmk/xap/gen_firmware/header_generator.py | 10 +++ .../qmk/xap/gen_firmware/inline_generator.py | 17 ++++ quantum/xap/xap.h | 1 + tmk_core/protocol/chibios/usb_main.c | 17 +++- tmk_core/protocol/lufa/lufa.c | 17 +++- tmk_core/protocol/vusb/vusb.c | 17 +++- 8 files changed, 162 insertions(+), 30 deletions(-) diff --git a/data/xap/xap_0.1.0.hjson b/data/xap/xap_0.1.0.hjson index 132fc421bc35..e9506b5c7b72 100755 --- a/data/xap/xap_0.1.0.hjson +++ b/data/xap/xap_0.1.0.hjson @@ -78,6 +78,28 @@ ''' } + type_definitions: { + broadcast_header: { + name: Broadcast Header + description: Packet format for broadcast messages. + type: struct + struct_members: [ + { + type: token + name: token + }, + { + type: u8 + name: type + }, + { + type: u8 + name: length + } + ] + } + } + broadcast_messages: { define_prefix: XAP_BROADCAST messages: { @@ -97,6 +119,15 @@ | **Value** | `0xFF` | `0xFF` | `0x00` | `0x0A`(10) | `0x48`(H) | `0x65`(e) | `0x6C`(l) | `0x6C`(l) | `0x6F`(o) | `0x20`( ) | `0x51`(Q) | `0x4D`(M) | `0x4B`(K) | `0x21`(!) | ''' } + 0x01: { + name: Secure Status + define: SECURE_STATUS + description: + ''' + Secure status has changed. + ''' + return_type: u8 + } } } diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index 5765bfeb8ab7..e61f7fe48bbd 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -48,7 +48,7 @@ def print_dotted_output(kb_info_json, prefix=''): cli.echo(' {fg_blue}%s{fg_reset}: %s', new_prefix, kb_info_json[key]) -def _xap_transaction(device, sub, route, ret_len, *args): +def _xap_transaction(device, sub, route, *args): # gen token tok = random.getrandbits(16) token = tok.to_bytes(2, byteorder='little') @@ -78,16 +78,20 @@ def _xap_transaction(device, sub, route, ret_len, *args): device.write(buffer) # get resp - array_alpha = device.read(4 + ret_len, 100) + array_alpha = device.read(64, 100) # validate tok sent == resp if str(token) != str(array_alpha[:2]): return None - return array_alpha[4:] + if int(array_alpha[2]) != 0x01: + return None + + payload_len = int(array_alpha[3]) + return array_alpha[4:4 + payload_len] def _query_device(device): - ver_data = _xap_transaction(device, 0x00, 0x00, 4) + ver_data = _xap_transaction(device, 0x00, 0x00) if not ver_data: return {'xap': 'UNKNOWN'} @@ -95,14 +99,14 @@ def _query_device(device): a = (ver_data[3] << 24) + (ver_data[2] << 16) + (ver_data[1] << 8) + (ver_data[0]) ver = f'{a>>24}.{a>>16 & 0xFF}.{a & 0xFFFF}' - secure = int.from_bytes(_xap_transaction(device, 0x00, 0x03, 1), 'little') + secure = int.from_bytes(_xap_transaction(device, 0x00, 0x03), 'little') secure = 'unlocked' if secure == 2 else 'LOCKED' return {'xap': ver, 'secure': secure} def _query_device_info_len(device): - len_data = _xap_transaction(device, 0x01, 0x05, 4) + len_data = _xap_transaction(device, 0x01, 0x05) if not len_data: return 0 @@ -111,7 +115,7 @@ def _query_device_info_len(device): def _query_device_info_chunk(device, offset): - return _xap_transaction(device, 0x01, 0x06, 32, offset) + return _xap_transaction(device, 0x01, 0x06, offset) def _query_device_info(device): @@ -143,7 +147,42 @@ def _list_devices(): # TODO: better formatting like "lsusb -v"? data = _query_device_info(device) print_dotted_output(data) - # _xap_transaction(device, 0x01, 0x07, 1) + + +def xap_doit(): + print("xap_doit") + # get layer count + # layers = _xap_transaction(device, 0x04, 0x01) + # layers = int.from_bytes(layers, "little") + # print(f'layers:{layers}') + + # get keycode [layer:0, row:0, col:0] + # keycode = _xap_transaction(device, 0x04, 0x02, b"\x00\x00\x00") + # keycode = int.from_bytes(keycode, "little") + # keycode_map = { + # # TODO: this should be data driven... + # 0x04: 'KC_A', + # 0x05: 'KC_B', + # 0x29: 'KC_ESCAPE' + # } + # print('keycode:' + keycode_map.get(keycode, 'unknown')) + + # Reboot + # _xap_transaction(device, 0x01, 0x07) + + +def xap_broadcast_listen(device): + try: + cli.log.info("Listening for XAP broadcasts...") + while 1: + array_alpha = device.read(64, 100) + if str(b"\xFF\xFF") == str(array_alpha[:2]): + if array_alpha[2] == 1: + cli.log.info(" Broadcast: Secure[%02x]", array_alpha[4]) + else: + cli.log.info(" Broadcast: type[%02x] data:[%02x]", array_alpha[2], array_alpha[4]) + except KeyboardInterrupt: + cli.log.info("Stopping...") @cli.argument('-d', '--device', help='device to select - uses format :.') @@ -161,25 +200,14 @@ def xap(cli): return _list_devices() # Connect to first available device - dev = _search()[0] + devices = _search() + if not devices: + cli.log.error("No devices found!") + return False + + dev = devices[0] device = hid.Device(path=dev['path']) cli.log.info("Connected to:%04x:%04x %s %s", dev['vendor_id'], dev['product_id'], dev['manufacturer_string'], dev['product_string']) - # get layer count - layers = _xap_transaction(device, 0x04, 0x01, 1) - layers = int.from_bytes(layers, "little") - print(f'layers:{layers}') - - # get keycode [layer:0, row:0, col:0] - keycode = _xap_transaction(device, 0x04, 0x02, 2, b"\x00\x00\x00") - keycode = int.from_bytes(keycode, "little") - keycode_map = { - # TODO: this should be data driven... - 0x04: 'KC_A', - 0x05: 'KC_B', - 0x29: 'KC_ESCAPE' - } - print('keycode:' + keycode_map.get(keycode, 'unknown')) - - # Reboot - # _xap_transaction(device, 0x01, 0x07, 1) + # xap_doit(device) + xap_broadcast_listen(device) diff --git a/lib/python/qmk/xap/gen_firmware/header_generator.py b/lib/python/qmk/xap/gen_firmware/header_generator.py index 6b589474715d..0af3b6d7c420 100755 --- a/lib/python/qmk/xap/gen_firmware/header_generator.py +++ b/lib/python/qmk/xap/gen_firmware/header_generator.py @@ -177,6 +177,16 @@ def _append_internal_types(lines, container): 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') + lines.append(f'#define {broadcast_prefix}_{define} {key}') + + # 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(): diff --git a/lib/python/qmk/xap/gen_firmware/inline_generator.py b/lib/python/qmk/xap/gen_firmware/inline_generator.py index 5b8117620294..074a37729d60 100755 --- a/lib/python/qmk/xap/gen_firmware/inline_generator.py +++ b/lib/python/qmk/xap/gen_firmware/inline_generator.py @@ -245,6 +245,22 @@ def _append_routing_tables(lines, container, container_id=None, route_stack=None 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): """Generates the XAP protocol header file, generated during normal build. """ @@ -254,6 +270,7 @@ def generate_inline(output_file): 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) diff --git a/quantum/xap/xap.h b/quantum/xap/xap.h index 7c43d90e278f..76e509549296 100644 --- a/quantum/xap/xap.h +++ b/quantum/xap/xap.h @@ -35,3 +35,4 @@ 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); diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index 53116d156219..af4eca4da0df 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -1137,7 +1137,7 @@ void xap_send_base(uint8_t *data, uint8_t length) { chnWrite(&drivers.xap_driver.driver, data, length); } -void xap_send(xap_token_t token, uint8_t response_flags, 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) { uint8_t rdata[XAP_EPSIZE] = {0}; xap_response_header_t *header = (xap_response_header_t *)&rdata[0]; header->token = token; @@ -1154,6 +1154,21 @@ void xap_send(xap_token_t token, uint8_t response_flags, const void *data, size_ 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]; diff --git a/tmk_core/protocol/lufa/lufa.c b/tmk_core/protocol/lufa/lufa.c index 969bf1ab21ad..0353ec7d4d51 100644 --- a/tmk_core/protocol/lufa/lufa.c +++ b/tmk_core/protocol/lufa/lufa.c @@ -243,7 +243,7 @@ void xap_send_base(uint8_t *data, uint8_t length) { Endpoint_SelectEndpoint(ep); } -void xap_send(xap_token_t token, uint8_t response_flags, 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) { uint8_t rdata[XAP_EPSIZE] = {0}; xap_response_header_t *header = (xap_response_header_t *)&rdata[0]; header->token = token; @@ -260,6 +260,21 @@ void xap_send(xap_token_t token, uint8_t response_flags, const void *data, size_ 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]; diff --git a/tmk_core/protocol/vusb/vusb.c b/tmk_core/protocol/vusb/vusb.c index 49d59f1574f9..f8375de36939 100644 --- a/tmk_core/protocol/vusb/vusb.c +++ b/tmk_core/protocol/vusb/vusb.c @@ -204,7 +204,7 @@ void xap_send_base(uint8_t *data, uint8_t length) { usbSetInterrupt4(0, 0); } -void xap_send(xap_token_t token, uint8_t response_flags, 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) { uint8_t rdata[XAP_BUFFER_SIZE] = {0}; xap_response_header_t *header = (xap_response_header_t *)&rdata[0]; header->token = token; @@ -221,6 +221,21 @@ void xap_send(xap_token_t token, uint8_t response_flags, const void *data, size_ 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]; From 194d6d65befe908baa5642e64361d77b57572bed Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 5 Apr 2022 19:17:48 +0100 Subject: [PATCH 046/203] format --- tmk_core/protocol/chibios/usb_main.c | 8 ++++---- tmk_core/protocol/lufa/lufa.c | 8 ++++---- tmk_core/protocol/vusb/vusb.c | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index af4eca4da0df..ffe1ba40cb0c 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -1155,10 +1155,10 @@ void xap_send(xap_token_t token, xap_response_flags_t response_flags, const void } 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; + 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; diff --git a/tmk_core/protocol/lufa/lufa.c b/tmk_core/protocol/lufa/lufa.c index 0353ec7d4d51..b9bfef0b979c 100644 --- a/tmk_core/protocol/lufa/lufa.c +++ b/tmk_core/protocol/lufa/lufa.c @@ -261,10 +261,10 @@ void xap_send(xap_token_t token, xap_response_flags_t response_flags, const void } 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; + 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; diff --git a/tmk_core/protocol/vusb/vusb.c b/tmk_core/protocol/vusb/vusb.c index f8375de36939..245e454cbec9 100644 --- a/tmk_core/protocol/vusb/vusb.c +++ b/tmk_core/protocol/vusb/vusb.c @@ -222,10 +222,10 @@ void xap_send(xap_token_t token, xap_response_flags_t response_flags, const void } 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; + 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; From 89fab427c4e1a3de0c8a54eb003d4f27d57c2e37 Mon Sep 17 00:00:00 2001 From: zvecr Date: Mon, 11 Apr 2022 00:43:18 +0100 Subject: [PATCH 047/203] stub out secure as its own feature --- builddefs/common_features.mk | 1 + builddefs/generic_features.mk | 1 + data/xap/xap_0.1.0.hjson | 16 +++++- lib/python/qmk/cli/xap/xap.py | 15 +++++- quantum/keyboard.c | 8 +++ quantum/process_keycode/process_secure.c | 18 +++++++ quantum/process_keycode/process_secure.h | 9 ++++ quantum/quantum.c | 3 ++ quantum/quantum.h | 5 ++ quantum/secure.c | 66 ++++++++++++++++++++++++ quantum/secure.h | 27 ++++++++++ quantum/xap/xap.c | 22 ++++++-- quantum/xap/xap.h | 2 + quantum/xap/xap_handlers.c | 16 ++++++ 14 files changed, 204 insertions(+), 5 deletions(-) create mode 100644 quantum/process_keycode/process_secure.c create mode 100644 quantum/process_keycode/process_secure.h create mode 100644 quantum/secure.c create mode 100644 quantum/secure.h diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk index 77d5aa25e3f0..52bf40228a65 100644 --- a/builddefs/common_features.mk +++ b/builddefs/common_features.mk @@ -802,6 +802,7 @@ ifeq ($(strip $(XAP_ENABLE)), yes) OPT_DEFS += -DXAP_ENABLE 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 diff --git a/builddefs/generic_features.mk b/builddefs/generic_features.mk index 53d4e16fd42b..0475a2ff09bc 100644 --- a/builddefs/generic_features.mk +++ b/builddefs/generic_features.mk @@ -32,6 +32,7 @@ GENERIC_FEATURES = \ KEY_OVERRIDE \ LEADER \ PROGRAMMABLE_BUTTON \ + SECURE \ SPACE_CADET \ SWAP_HANDS \ TAP_DANCE \ diff --git a/data/xap/xap_0.1.0.hjson b/data/xap/xap_0.1.0.hjson index e9506b5c7b72..3d094eb375c3 100755 --- a/data/xap/xap_0.1.0.hjson +++ b/data/xap/xap_0.1.0.hjson @@ -172,7 +172,21 @@ * any other value should be interpreted as disabled ''' return_type: u8 - return_value: secure_status + 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 + description: Disable secure routes + return_execute: secure_lock } } }, diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index e61f7fe48bbd..36979bf47cca 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -185,9 +185,14 @@ def xap_broadcast_listen(device): cli.log.info("Stopping...") +def xap_unlock(device): + _xap_transaction(device, 0x00, 0x04) + + @cli.argument('-d', '--device', help='device to select - uses format :.') @cli.argument('-i', '--index', default=0, help='device index to select.') @cli.argument('-l', '--list', arg_only=True, action='store_true', help='List available devices.') +@cli.argument('action', nargs='?', 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 @@ -210,4 +215,12 @@ def xap(cli): cli.log.info("Connected to:%04x:%04x %s %s", dev['vendor_id'], dev['product_id'], dev['manufacturer_string'], dev['product_string']) # xap_doit(device) - xap_broadcast_listen(device) + if cli.args.action == 'unlock': + xap_unlock(device) + cli.log.info("Done") + + elif cli.args.action == 'listen': + xap_broadcast_listen(device) + + elif not cli.args.action: + xap_broadcast_listen(device) \ No newline at end of file diff --git a/quantum/keyboard.c b/quantum/keyboard.c index fc8a2fe8e391..5a80330b550f 100644 --- a/quantum/keyboard.c +++ b/quantum/keyboard.c @@ -562,6 +562,14 @@ void quantum_task(void) { #ifdef AUTO_SHIFT_ENABLE autoshift_matrix_scan(); #endif + +#ifdef SECURE_ENABLE + secure_task(); +#endif + +#ifdef XAP_ENABLE + xap_event_task(); +#endif } /** \brief Keyboard task: Do keyboard routine jobs diff --git a/quantum/process_keycode/process_secure.c b/quantum/process_keycode/process_secure.c new file mode 100644 index 000000000000..af0690634409 --- /dev/null +++ b/quantum/process_keycode/process_secure.c @@ -0,0 +1,18 @@ +// Copyright 2022 QMK +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "secure.h" +#include "process_secure.h" + +bool process_secure(uint16_t keycode, keyrecord_t *record) { + if (secure_is_unlocking()) { + if (!record->event.pressed) { + secure_keypress_event(record->event.key.row, record->event.key.col); + } + + // Normal keypresses should be disabled until the sequence is completed + return false; + } + + return true; +} diff --git a/quantum/process_keycode/process_secure.h b/quantum/process_keycode/process_secure.h new file mode 100644 index 000000000000..7f6821f0d8f8 --- /dev/null +++ b/quantum/process_keycode/process_secure.h @@ -0,0 +1,9 @@ +// Copyright 2022 QMK +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "action.h" + +bool process_secure(uint16_t keycode, keyrecord_t *record); diff --git a/quantum/quantum.c b/quantum/quantum.c index d4e91ddd37fb..eedc004a3037 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -245,6 +245,9 @@ bool process_record_quantum(keyrecord_t *record) { #endif #if defined(VIA_ENABLE) process_record_via(keycode, record) && +#endif +#if defined(SECURE_ENABLE) + process_secure(keycode, record) && #endif process_record_kb(keycode, record) && #if defined(SEQUENCER_ENABLE) diff --git a/quantum/quantum.h b/quantum/quantum.h index ff3679bb5e80..122e886ad2ef 100644 --- a/quantum/quantum.h +++ b/quantum/quantum.h @@ -196,6 +196,11 @@ extern layer_state_t layer_state; # include "process_dynamic_macro.h" #endif +#ifdef SECURE_ENABLE +# include "secure.h" +# include "process_secure.h" +#endif + #ifdef DYNAMIC_KEYMAP_ENABLE # include "dynamic_keymap.h" #endif diff --git a/quantum/secure.c b/quantum/secure.c new file mode 100644 index 000000000000..26a3246ccfca --- /dev/null +++ b/quantum/secure.c @@ -0,0 +1,66 @@ +// Copyright 2022 QMK +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "secure.h" +#include "timer.h" + +#ifndef SECURE_UNLOCK_TIMEOUT +# define SECURE_UNLOCK_TIMEOUT 5000 +#endif + +#ifndef SECURE_IDLE_TIMEOUT +# define SECURE_IDLE_TIMEOUT 60000 +#endif + +secure_status_t secure_status = SECURE_LOCKED; +static uint32_t unlock_time = 0; +static uint32_t idle_time = 0; + +secure_status_t secure_get_status(void) { + return secure_status; +} + +bool secure_is_unlocking(void) { + return secure_status == SECURE_PENDING; +} + +void secure_lock(void) { + secure_status = SECURE_LOCKED; +} + +void secure_unlock(void) { + secure_status = SECURE_UNLOCKED; + idle_time = timer_read32(); +} + +void secure_request_unlock(void) { + if (secure_status == SECURE_LOCKED) { + secure_status = SECURE_PENDING; + unlock_time = timer_read32(); + } +} + +void secure_keypress_event(uint8_t row, uint8_t col) { + // TODO: check keypress is actually part of unlock sequence + secure_unlock(); +} + +void secure_task(void) { +#if SECURE_UNLOCK_TIMEOUT != 0 + // handle unlock timeout + if (secure_status == SECURE_PENDING) { + if (timer_elapsed32(unlock_time) >= SECURE_UNLOCK_TIMEOUT) { + secure_lock(); + } + } +#endif + +#if SECURE_IDLE_TIMEOUT != 0 + // handle idle timeout + if (secure_status == SECURE_UNLOCKED) { + if (timer_elapsed32(idle_time) >= SECURE_IDLE_TIMEOUT) { + secure_lock(); + } + } +#endif +} diff --git a/quantum/secure.h b/quantum/secure.h new file mode 100644 index 000000000000..ba5fd6cbff7a --- /dev/null +++ b/quantum/secure.h @@ -0,0 +1,27 @@ +// Copyright 2022 QMK +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +typedef enum { + SECURE_LOCKED, + SECURE_PENDING, + SECURE_UNLOCKED, +} secure_status_t; + +secure_status_t secure_get_status(void); + +bool secure_is_unlocking(void); + +void secure_lock(void); + +void secure_unlock(void); + +void secure_request_unlock(void); + +void secure_keypress_event(uint8_t row, uint8_t col); + +void secure_task(void); diff --git a/quantum/xap/xap.c b/quantum/xap/xap.c index 00901b0423e8..806ce7f1602b 100644 --- a/quantum/xap/xap.c +++ b/quantum/xap/xap.c @@ -16,6 +16,7 @@ #include #include +#include "secure.h" #include "info_json_gz.h" bool get_info_json_chunk(uint16_t offset, uint8_t *data, uint8_t data_len) { @@ -27,8 +28,6 @@ bool get_info_json_chunk(uint16_t offset, uint8_t *data, uint8_t data_len) { return true; } -uint8_t secure_status = 2; - #define QSTR2(z) #z #define QSTR(z) QSTR2(z) @@ -86,11 +85,18 @@ void xap_execute_route(xap_token_t token, const xap_route_t *routes, size_t max_ xap_route_t route; memcpy_P(&route, &routes[id], sizeof(xap_route_t)); - if (route.flags.is_secure && secure_status != 2) { + if (route.flags.is_secure && secure_get_status() != SECURE_UNLOCKED) { xap_respond_failure(token, XAP_RESPONSE_FLAG_SECURE_FAILURE); return; } + // TODO: All other subsystems are disabled during unlock. + // how to flag status route as still allowed? + // if (!route.flags.is_secure && secure_get_status() == SECURE_PENDING) { + // xap_respond_failure(token, XAP_RESPONSE_FLAG_UNLOCK_IN_PROGRESS); + // return; + // } + switch (route.flags.type) { case XAP_ROUTE: if (route.child_routes != NULL && route.child_routes_len > 0 && data_len > 0) { @@ -134,3 +140,13 @@ void xap_execute_route(xap_token_t token, const xap_route_t *routes, size_t max_ 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); } + +void xap_event_task(void) { + static secure_status_t last_status = -1; + + secure_status_t status = secure_get_status(); + if (last_status != status) { + last_status = status; + xap_broadcast_secure_status(status); + } +} diff --git a/quantum/xap/xap.h b/quantum/xap/xap.h index 76e509549296..b1ecd8cb9ca2 100644 --- a/quantum/xap/xap.h +++ b/quantum/xap/xap.h @@ -36,3 +36,5 @@ 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); + +void xap_event_task(void); diff --git a/quantum/xap/xap_handlers.c b/quantum/xap/xap_handlers.c index 70cd2fb2726b..e16a586dbb2d 100644 --- a/quantum/xap/xap_handlers.c +++ b/quantum/xap/xap_handlers.c @@ -16,6 +16,7 @@ #include #include +#include "secure.h" void xap_respond_success(xap_token_t token) { xap_send(token, XAP_RESPONSE_FLAG_SUCCESS, NULL, 0); @@ -59,6 +60,21 @@ bool xap_respond_get_info_json_chunk(xap_token_t token, const void *data, size_t 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); +} + // TODO: how to set this if "custom" is just an empty stub #ifndef BOOTLOADER_JUMP_SUPPORTED # define BOOTLOADER_JUMP_SUPPORTED From 3e4de1ebd0287b4ce2f4b184a91a6060d99e10a1 Mon Sep 17 00:00:00 2001 From: zvecr Date: Mon, 11 Apr 2022 00:53:31 +0100 Subject: [PATCH 048/203] format --- lib/python/qmk/cli/xap/xap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index 36979bf47cca..9ab41e3ee3df 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -223,4 +223,4 @@ def xap(cli): xap_broadcast_listen(device) elif not cli.args.action: - xap_broadcast_listen(device) \ No newline at end of file + xap_broadcast_listen(device) From 79db2df2283b7b2da7be2760337238ab308c1034 Mon Sep 17 00:00:00 2001 From: zvecr Date: Mon, 11 Apr 2022 01:07:22 +0100 Subject: [PATCH 049/203] Allow secure to be slightly more optional --- quantum/xap/xap.c | 6 +++++- quantum/xap/xap_handlers.c | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/quantum/xap/xap.c b/quantum/xap/xap.c index 806ce7f1602b..15b56297c9a0 100644 --- a/quantum/xap/xap.c +++ b/quantum/xap/xap.c @@ -69,7 +69,7 @@ struct __attribute__((packed)) xap_route_t { // XAP_VALUE / XAP_CONST_MEM struct { - const void * const_data; + const void *const_data; const uint8_t const_data_len; }; }; @@ -85,6 +85,7 @@ void xap_execute_route(xap_token_t token, const xap_route_t *routes, size_t max_ xap_route_t route; memcpy_P(&route, &routes[id], sizeof(xap_route_t)); +#ifdef SECURE_ENABLE if (route.flags.is_secure && secure_get_status() != SECURE_UNLOCKED) { xap_respond_failure(token, XAP_RESPONSE_FLAG_SECURE_FAILURE); return; @@ -96,6 +97,7 @@ void xap_execute_route(xap_token_t token, const xap_route_t *routes, size_t max_ // xap_respond_failure(token, XAP_RESPONSE_FLAG_UNLOCK_IN_PROGRESS); // return; // } +#endif switch (route.flags.type) { case XAP_ROUTE: @@ -142,6 +144,7 @@ void xap_receive(xap_token_t token, const uint8_t *data, size_t length) { } void xap_event_task(void) { +#ifdef SECURE_ENABLE static secure_status_t last_status = -1; secure_status_t status = secure_get_status(); @@ -149,4 +152,5 @@ void xap_event_task(void) { last_status = status; xap_broadcast_secure_status(status); } +#endif } diff --git a/quantum/xap/xap_handlers.c b/quantum/xap/xap_handlers.c index e16a586dbb2d..8d0b2e472509 100644 --- a/quantum/xap/xap_handlers.c +++ b/quantum/xap/xap_handlers.c @@ -16,7 +16,13 @@ #include #include + #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); From 05b5a4c23aa8ddf2505f7ca1c3753a902869a228 Mon Sep 17 00:00:00 2001 From: zvecr Date: Mon, 11 Apr 2022 01:08:37 +0100 Subject: [PATCH 050/203] format --- quantum/xap/xap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quantum/xap/xap.c b/quantum/xap/xap.c index 15b56297c9a0..bda2846a2f69 100644 --- a/quantum/xap/xap.c +++ b/quantum/xap/xap.c @@ -69,7 +69,7 @@ struct __attribute__((packed)) xap_route_t { // XAP_VALUE / XAP_CONST_MEM struct { - const void *const_data; + const void * const_data; const uint8_t const_data_len; }; }; From ffb0575eb81f989d0ba034f9ce9d74000bfb7844 Mon Sep 17 00:00:00 2001 From: zvecr Date: Mon, 11 Apr 2022 01:59:48 +0100 Subject: [PATCH 051/203] stub out secure data driven config --- data/mappings/info_config.json | 2 ++ data/mappings/info_rules.json | 1 + data/schemas/keyboard.jsonschema | 24 ++++++++++++++++++++ lib/python/qmk/info.py | 39 ++++++++++++++++++++++++++++++++ quantum/secure.c | 16 ++++++++++++- 5 files changed, 81 insertions(+), 1 deletion(-) diff --git a/data/mappings/info_config.json b/data/mappings/info_config.json index 2121741d1996..ccc9cdbb5a80 100644 --- a/data/mappings/info_config.json +++ b/data/mappings/info_config.json @@ -78,6 +78,8 @@ "QMK_KEYS_PER_SCAN": {"info_key": "qmk.keys_per_scan", "value_type": "int"}, "QMK_LED": {"info_key": "qmk_lufa_bootloader.led"}, "QMK_SPEAKER": {"info_key": "qmk_lufa_bootloader.speaker"}, + "SECURE_UNLOCK_TIMEOUT": {"info_key": "secure.unlock_timeout", "value_type": "int"}, + "SECURE_IDLE_TIMEOUT": {"info_key": "secure.idle_timeout", "value_type": "int"}, "SENDSTRING_BELL": {"info_key": "audio.macro_beep", "value_type": "bool"}, "SPLIT_MODS_ENABLE": {"info_key": "split.transport.sync_modifiers", "value_type": "bool"}, "SPLIT_TRANSPORT_MIRROR": {"info_key": "split.transport.sync_matrix_state", "value_type": "bool"}, diff --git a/data/mappings/info_rules.json b/data/mappings/info_rules.json index 237e9f10246e..4b0fde562979 100644 --- a/data/mappings/info_rules.json +++ b/data/mappings/info_rules.json @@ -20,6 +20,7 @@ "MOUSEKEY_ENABLE": {"info_key": "mouse_key.enabled", "value_type": "bool"}, "NO_USB_STARTUP_CHECK": {"info_key": "usb.no_startup_check", "value_type": "bool"}, "PIN_COMPATIBLE": {"info_key": "pin_compatible"}, + "SECURE_ENABLE": {"info_key": "secure.enabled", "value_type": "bool"}, "SPLIT_KEYBOARD": {"info_key": "split.enabled", "value_type": "bool"}, "SPLIT_TRANSPORT": {"info_key": "split.transport.protocol", "to_c": false}, "WAIT_FOR_USB": {"info_key": "usb.wait_for", "value_type": "bool"} diff --git a/data/schemas/keyboard.jsonschema b/data/schemas/keyboard.jsonschema index dce2e855a145..9cde658128b3 100644 --- a/data/schemas/keyboard.jsonschema +++ b/data/schemas/keyboard.jsonschema @@ -300,6 +300,30 @@ } } }, + "secure": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": {"type": "boolean"}, + "unlock_timeout": {"$ref": "qmk.definitions.v1#/unsigned_int"}, + "idle_timeout": {"$ref": "qmk.definitions.v1#/unsigned_int"}, + "unlock_sequence": { + "type": "array", + "minLength": 1, + "maxLength": 5, + "items": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "number", + "min": 0, + "multipleOf": 1 + } + } + } + } + }, "split": { "type": "object", "additionalProperties": false, diff --git a/lib/python/qmk/info.py b/lib/python/qmk/info.py index 060404667d16..799050451799 100644 --- a/lib/python/qmk/info.py +++ b/lib/python/qmk/info.py @@ -171,6 +171,29 @@ def _extract_pins(pins): return [_pin_name(pin) for pin in pins.split(',')] +def _parse_2d_array(raw): + """Return a 2d array of ints + """ + out_array = [] + + while raw[-1] != '}': + raw = raw[:-1] + + for row in raw.split('},{'): + if row.startswith('{'): + row = row[1:] + + if row.endswith('}'): + row = row[:-1] + + out_array.append([]) + + for val in row.split(','): + out_array[-1].append(int(val)) + + return out_array + + def _extract_direct_matrix(direct_pins): """ """ @@ -210,6 +233,21 @@ def _extract_audio(info_data, config_c): info_data['audio'] = {'pins': audio_pins} +def _extract_secure_unlock(info_data, config_c): + """Populate data about the secure unlock sequence + """ + unlock = config_c.get('SECURE_UNLOCK_SEQUENCE', '').replace(' ', '')[1:-1] + if unlock: + unlock_array = _parse_2d_array(unlock) + if 'secure' not in info_data: + info_data['secure'] = {} + + if 'unlock_sequence' in info_data['secure']: + _log_warning(info_data, 'Secure unlock sequence is specified in both config.h (SECURE_UNLOCK_SEQUENCE) and info.json (secure.unlock_sequence) (Value: %s), the config.h value wins.' % info_data['secure']['unlock_sequence']) + + info_data['secure']['unlock_sequence'] = unlock_array + + def _extract_split_main(info_data, config_c): """Populate data about the split configuration """ @@ -469,6 +507,7 @@ def _extract_config_h(info_data, config_c): # Pull data that easily can't be mapped in json _extract_matrix_info(info_data, config_c) _extract_audio(info_data, config_c) + _extract_secure_unlock(info_data, config_c) _extract_split_main(info_data, config_c) _extract_split_transport(info_data, config_c) _extract_split_right_pins(info_data, config_c) diff --git a/quantum/secure.c b/quantum/secure.c index 26a3246ccfca..751977d351cc 100644 --- a/quantum/secure.c +++ b/quantum/secure.c @@ -12,6 +12,13 @@ # define SECURE_IDLE_TIMEOUT 60000 #endif +#ifndef SECURE_UNLOCK_SEQUENCE +# define SECURE_UNLOCK_SEQUENCE \ + { \ + { 0, 0 } \ + } +#endif + secure_status_t secure_status = SECURE_LOCKED; static uint32_t unlock_time = 0; static uint32_t idle_time = 0; @@ -41,8 +48,15 @@ void secure_request_unlock(void) { } void secure_keypress_event(uint8_t row, uint8_t col) { + static const uint8_t sequence[][2] = SECURE_UNLOCK_SEQUENCE; + // TODO: check keypress is actually part of unlock sequence - secure_unlock(); + uint8_t offset = 0; + if ((sequence[offset][0] == row) && (sequence[offset][1] == col)) { + secure_unlock(); + } else { + secure_lock(); + } } void secure_task(void) { From 1ea2928d2a9307427afce68c654b3df63c1e205e Mon Sep 17 00:00:00 2001 From: zvecr Date: Mon, 11 Apr 2022 02:14:19 +0100 Subject: [PATCH 052/203] XAP messages extend timeout? --- quantum/secure.c | 6 ++++++ quantum/secure.h | 2 ++ quantum/xap/xap.c | 3 +++ 3 files changed, 11 insertions(+) diff --git a/quantum/secure.c b/quantum/secure.c index 751977d351cc..375f75fb008e 100644 --- a/quantum/secure.c +++ b/quantum/secure.c @@ -47,6 +47,12 @@ void secure_request_unlock(void) { } } +void secure_activity_event(void) { + if (secure_status == SECURE_UNLOCKED) { + idle_time = timer_read32(); + } +} + void secure_keypress_event(uint8_t row, uint8_t col) { static const uint8_t sequence[][2] = SECURE_UNLOCK_SEQUENCE; diff --git a/quantum/secure.h b/quantum/secure.h index ba5fd6cbff7a..e1e3067c550f 100644 --- a/quantum/secure.h +++ b/quantum/secure.h @@ -22,6 +22,8 @@ void secure_unlock(void); void secure_request_unlock(void); +void secure_activity_event(void); + void secure_keypress_event(uint8_t row, uint8_t col); void secure_task(void); diff --git a/quantum/xap/xap.c b/quantum/xap/xap.c index bda2846a2f69..61e0d4de3080 100644 --- a/quantum/xap/xap.c +++ b/quantum/xap/xap.c @@ -97,6 +97,9 @@ void xap_execute_route(xap_token_t token, const xap_route_t *routes, size_t max_ // xap_respond_failure(token, XAP_RESPONSE_FLAG_UNLOCK_IN_PROGRESS); // return; // } + + // TODO: XAP messages extend timeout? + secure_activity_event(); #endif switch (route.flags.type) { From d19285019d7f9a868f3bd603aa3d7b05c87c9eb3 Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 12 Apr 2022 01:37:25 +0100 Subject: [PATCH 053/203] All other subsystems are disabled during unlock --- data/xap/xap_0.1.0.hjson | 4 +- lib/python/qmk/cli/xap/xap.py | 2 +- .../qmk/xap/gen_firmware/inline_generator.py | 11 ++++- quantum/secure.c | 4 -- quantum/secure.h | 40 ++++++++++++++++- quantum/xap/xap.c | 45 ++++++++++++------- 6 files changed, 81 insertions(+), 25 deletions(-) diff --git a/data/xap/xap_0.1.0.hjson b/data/xap/xap_0.1.0.hjson index 3d094eb375c3..6835ec05bd7e 100755 --- a/data/xap/xap_0.1.0.hjson +++ b/data/xap/xap_0.1.0.hjson @@ -171,6 +171,7 @@ * 2 means secure routes are allowed * any other value should be interpreted as disabled ''' + permissions: ignore return_type: u8 return_execute: secure_status } @@ -185,6 +186,7 @@ type: command name: Secure Lock define: SECURE_LOCK + permissions: ignore description: Disable secure routes return_execute: secure_lock } @@ -301,7 +303,7 @@ type: command name: Jump to bootloader define: BOOTLOADER_JUMP - secure: true + permissions: secure enable_if_preprocessor: defined(BOOTLOADER_JUMP_SUPPORTED) description: ''' diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index 9ab41e3ee3df..57f93afaf1eb 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -93,7 +93,7 @@ def _xap_transaction(device, sub, route, *args): def _query_device(device): ver_data = _xap_transaction(device, 0x00, 0x00) if not ver_data: - return {'xap': 'UNKNOWN'} + return {'xap': 'UNKNOWN', 'secure': 'UNKNOWN'} # to u32 to BCD string a = (ver_data[3] << 24) + (ver_data[2] << 16) + (ver_data[1] << 8) + (ver_data[0]) diff --git a/lib/python/qmk/xap/gen_firmware/inline_generator.py b/lib/python/qmk/xap/gen_firmware/inline_generator.py index 074a37729d60..8aeb35604776 100755 --- a/lib/python/qmk/xap/gen_firmware/inline_generator.py +++ b/lib/python/qmk/xap/gen_firmware/inline_generator.py @@ -125,10 +125,17 @@ def _append_routing_table_declaration(lines, container, container_id, route_stac def _append_routing_table_entry_flags(lines, container, container_id, route_stack): - is_secure = 1 if ('secure' in container and container['secure'] is True) else 0 + 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' .is_secure = {is_secure},') + lines.append(f' .secure = {is_secure},') lines.append(' },') diff --git a/quantum/secure.c b/quantum/secure.c index 375f75fb008e..7eab865b9f22 100644 --- a/quantum/secure.c +++ b/quantum/secure.c @@ -27,10 +27,6 @@ secure_status_t secure_get_status(void) { return secure_status; } -bool secure_is_unlocking(void) { - return secure_status == SECURE_PENDING; -} - void secure_lock(void) { secure_status = SECURE_LOCKED; } diff --git a/quantum/secure.h b/quantum/secure.h index e1e3067c550f..04507fd5b139 100644 --- a/quantum/secure.h +++ b/quantum/secure.h @@ -3,27 +3,65 @@ #pragma once +/** \file + * + * Exposes a set of functionality to act as a virtual padlock for your device + * ... As long as that padlock is made of paper and its currently raining. + */ + #include #include +/** \brief Available secure states + */ typedef enum { SECURE_LOCKED, SECURE_PENDING, SECURE_UNLOCKED, } secure_status_t; +/** \brief Query current secure state + */ secure_status_t secure_get_status(void); -bool secure_is_unlocking(void); +/** \brief Helper to check if unlocking is currently locked + */ +#define secure_is_locked() (secure_get_status() == SECURE_LOCKED) + +/** \brief Helper to check if unlocking is currently in progress + */ +#define secure_is_unlocking() (secure_get_status() == SECURE_PENDING) + +/** \brief Helper to check if unlocking is currently unlocked + */ +#define secure_is_unlocked() (secure_get_status() == SECURE_UNLOCKED) +/** \brief Lock down the device + */ void secure_lock(void); +/** \brief Force unlock the device + * + * \warning bypasses user unlock sequence + */ void secure_unlock(void); +/** \brief Begin listening for an unlock sequence + */ void secure_request_unlock(void); +/** \brief Flag to the secure subsystem that user activity has happened + * + * Call when some user activity has happened and the device should remain unlocked + */ void secure_activity_event(void); +/** \brief Flag to the secure subsystem that user has triggered a keypress + * + * Call to trigger processing of the unlock sequence + */ void secure_keypress_event(uint8_t row, uint8_t col); +/** \brief Handle various secure subsystem background tasks + */ void secure_task(void); diff --git a/quantum/xap/xap.c b/quantum/xap/xap.c index 61e0d4de3080..d4b6eb80fa7e 100644 --- a/quantum/xap/xap.c +++ b/quantum/xap/xap.c @@ -43,9 +43,17 @@ typedef enum 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; - uint8_t is_secure : 1; + 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."); @@ -77,6 +85,24 @@ struct __attribute__((packed)) xap_route_t { #include +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]; @@ -85,23 +111,10 @@ void xap_execute_route(xap_token_t token, const xap_route_t *routes, size_t max_ xap_route_t route; memcpy_P(&route, &routes[id], sizeof(xap_route_t)); -#ifdef SECURE_ENABLE - if (route.flags.is_secure && secure_get_status() != SECURE_UNLOCKED) { - xap_respond_failure(token, XAP_RESPONSE_FLAG_SECURE_FAILURE); + if (xap_pre_execute_route(token, &route)) { return; } - // TODO: All other subsystems are disabled during unlock. - // how to flag status route as still allowed? - // if (!route.flags.is_secure && secure_get_status() == SECURE_PENDING) { - // xap_respond_failure(token, XAP_RESPONSE_FLAG_UNLOCK_IN_PROGRESS); - // return; - // } - - // TODO: XAP messages extend timeout? - secure_activity_event(); -#endif - switch (route.flags.type) { case XAP_ROUTE: if (route.child_routes != NULL && route.child_routes_len > 0 && data_len > 0) { From 320f161c7282c4de797bf3765df74571326fe174 Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 12 Apr 2022 01:49:30 +0100 Subject: [PATCH 054/203] Process entire unlock sequence --- quantum/secure.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/quantum/secure.c b/quantum/secure.c index 7eab865b9f22..4409cb1b2a73 100644 --- a/quantum/secure.c +++ b/quantum/secure.c @@ -15,7 +15,9 @@ #ifndef SECURE_UNLOCK_SEQUENCE # define SECURE_UNLOCK_SEQUENCE \ { \ - { 0, 0 } \ + {0, 0}, { \ + 0, 1 \ + } \ } #endif @@ -51,12 +53,17 @@ void secure_activity_event(void) { void secure_keypress_event(uint8_t row, uint8_t col) { static const uint8_t sequence[][2] = SECURE_UNLOCK_SEQUENCE; + static const uint8_t sequence_len = sizeof(sequence) / sizeof(sequence[0]); - // TODO: check keypress is actually part of unlock sequence - uint8_t offset = 0; + static uint8_t offset = 0; if ((sequence[offset][0] == row) && (sequence[offset][1] == col)) { - secure_unlock(); + offset++; + if (offset == sequence_len) { + offset = 0; + secure_unlock(); + } } else { + offset = 0; secure_lock(); } } From 12e8c8d8eee2d8f61e8432d7f2a784c635894be1 Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 12 Apr 2022 01:58:02 +0100 Subject: [PATCH 055/203] Process entire unlock sequence - revert changes to SECURE_UNLOCK_SEQUENCE --- quantum/secure.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/quantum/secure.c b/quantum/secure.c index 4409cb1b2a73..46b8badaccea 100644 --- a/quantum/secure.c +++ b/quantum/secure.c @@ -15,9 +15,7 @@ #ifndef SECURE_UNLOCK_SEQUENCE # define SECURE_UNLOCK_SEQUENCE \ { \ - {0, 0}, { \ - 0, 1 \ - } \ + { 0, 0 } \ } #endif @@ -60,7 +58,7 @@ void secure_keypress_event(uint8_t row, uint8_t col) { offset++; if (offset == sequence_len) { offset = 0; - secure_unlock(); + secure_unlock(); } } else { offset = 0; From f249e33f7015eaba4cc2bae5baab5a5f34b87a07 Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 12 Apr 2022 02:08:18 +0100 Subject: [PATCH 056/203] Short term bodge to force dynamic_keymap population --- quantum/eeconfig.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/quantum/eeconfig.c b/quantum/eeconfig.c index 0ff9996ca413..c829e9d263ed 100644 --- a/quantum/eeconfig.c +++ b/quantum/eeconfig.c @@ -81,6 +81,10 @@ void eeconfig_init_quantum(void) { // properly re-initialized. via_eeprom_set_valid(false); eeconfig_init_via(); +#elif defined(XAP_ENABLE) + // TODO: define XAP reset behaviour + void dynamic_keymap_reset(void); + dynamic_keymap_reset(); #endif eeconfig_init_kb(); From 2563e7b2a0435d56fe7a7e04ab30277ce9df5400 Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 12 Apr 2022 13:53:12 +0100 Subject: [PATCH 057/203] format --- quantum/secure.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quantum/secure.c b/quantum/secure.c index 46b8badaccea..28ffa6bcdeb8 100644 --- a/quantum/secure.c +++ b/quantum/secure.c @@ -58,7 +58,7 @@ void secure_keypress_event(uint8_t row, uint8_t col) { offset++; if (offset == sequence_len) { offset = 0; - secure_unlock(); + secure_unlock(); } } else { offset = 0; From b0b6594ded3148e9eebd68358ccc5e7ee9d57e86 Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 13 Apr 2022 00:37:44 +0100 Subject: [PATCH 058/203] secure keycodes? --- quantum/process_keycode/process_secure.c | 23 ++++++++++++++++++++++- quantum/process_keycode/process_secure.h | 2 ++ quantum/quantum.c | 8 +++++++- quantum/quantum_keycodes.h | 4 ++++ 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/quantum/process_keycode/process_secure.c b/quantum/process_keycode/process_secure.c index af0690634409..27f4a439bcca 100644 --- a/quantum/process_keycode/process_secure.c +++ b/quantum/process_keycode/process_secure.c @@ -3,8 +3,9 @@ #include "secure.h" #include "process_secure.h" +#include "quantum_keycodes.h" -bool process_secure(uint16_t keycode, keyrecord_t *record) { +bool preprocess_secure(uint16_t keycode, keyrecord_t *record) { if (secure_is_unlocking()) { if (!record->event.pressed) { secure_keypress_event(record->event.key.row, record->event.key.col); @@ -16,3 +17,23 @@ bool process_secure(uint16_t keycode, keyrecord_t *record) { return true; } + +bool process_secure(uint16_t keycode, keyrecord_t *record) { +#ifndef SECURE_DISABLE_KEYCODES + if (!record->event.pressed) { + if (keycode == SECURE_LOCK) { + secure_lock(); + return false; + } + if (keycode == SECURE_UNLOCK) { + secure_lock(); + return false; + } + if (keycode == SECURE_TOGGLE) { + secure_is_locked() ? secure_unlock() : secure_lock(); + return false; + } + } +#endif + return true; +} \ No newline at end of file diff --git a/quantum/process_keycode/process_secure.h b/quantum/process_keycode/process_secure.h index 7f6821f0d8f8..a39076ebfa24 100644 --- a/quantum/process_keycode/process_secure.h +++ b/quantum/process_keycode/process_secure.h @@ -6,4 +6,6 @@ #include #include "action.h" +bool preprocess_secure(uint16_t keycode, keyrecord_t *record); + bool process_secure(uint16_t keycode, keyrecord_t *record); diff --git a/quantum/quantum.c b/quantum/quantum.c index eedc004a3037..673ea91b11f9 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -212,6 +212,12 @@ bool process_record_quantum(keyrecord_t *record) { // return false; // } +#if defined(SECURE_ENABLE) + if (!preprocess_secure(keycode, record)) { + return false; + } +#endif + #ifdef VELOCIKEY_ENABLE if (velocikey_enabled() && record->event.pressed) { velocikey_accelerate(); @@ -246,10 +252,10 @@ bool process_record_quantum(keyrecord_t *record) { #if defined(VIA_ENABLE) process_record_via(keycode, record) && #endif + process_record_kb(keycode, record) && #if defined(SECURE_ENABLE) process_secure(keycode, record) && #endif - process_record_kb(keycode, record) && #if defined(SEQUENCER_ENABLE) process_sequencer(keycode, record) && #endif diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h index dacfe5bdcd27..c7b4ea593feb 100644 --- a/quantum/quantum_keycodes.h +++ b/quantum/quantum_keycodes.h @@ -597,6 +597,10 @@ enum quantum_keycodes { QK_MAKE, + SECURE_LOCK, + SECURE_UNLOCK, + SECURE_TOGGLE, + // Start of custom keycode range for keyboards and keymaps - always leave at the end SAFE_RANGE }; From 1d96fc866d520cf032379c1bb7674fcfd98067d8 Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 19 Apr 2022 00:04:22 +0100 Subject: [PATCH 059/203] Add route for hardware_id --- data/xap/xap_0.1.0.hjson | 8 ++++++++ lib/python/qmk/cli/xap/xap.py | 8 ++++++++ lib/python/qmk/xap/gen_firmware/header_generator.py | 6 ++++-- quantum/xap/xap_handlers.c | 6 ++++++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/data/xap/xap_0.1.0.hjson b/data/xap/xap_0.1.0.hjson index 6835ec05bd7e..7ad69aafba4b 100755 --- a/data/xap/xap_0.1.0.hjson +++ b/data/xap/xap_0.1.0.hjson @@ -317,6 +317,14 @@ return_type: u8 return_execute: request_bootloader_jump } + 0x08: { + type: command + name: info.json + define: HARDWARE_ID + description: Retrieves a unique identifier for the board. + return_type: u32[4] + return_execute: get_hardware_id + } } }, diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index 57f93afaf1eb..71ff23a9a4a8 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -35,6 +35,9 @@ def print_dotted_output(kb_info_json, prefix=''): 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): @@ -105,6 +108,10 @@ def _query_device(device): return {'xap': ver, 'secure': secure} +def _query_device_id(device): + return _xap_transaction(device, 0x01, 0x08) + + def _query_device_info_len(device): len_data = _xap_transaction(device, 0x01, 0x05) if not len_data: @@ -146,6 +153,7 @@ def _list_devices(): if cli.config.general.verbose: # TODO: better formatting like "lsusb -v"? data = _query_device_info(device) + data["_id"] = _query_device_id(device) print_dotted_output(data) diff --git a/lib/python/qmk/xap/gen_firmware/header_generator.py b/lib/python/qmk/xap/gen_firmware/header_generator.py index 0af3b6d7c420..292cd4a7b3d4 100755 --- a/lib/python/qmk/xap/gen_firmware/header_generator.py +++ b/lib/python/qmk/xap/gen_firmware/header_generator.py @@ -150,8 +150,10 @@ def _append_route_types(lines, container, container_id=None, route_stack=None): elif 'return_type' in container: return_type = container['return_type'] - if return_type == 'u8[32]': - lines.append(f'typedef struct {{ uint8_t x[32]; }} {route_name}_t;') + found = re.search(r'(u\d+)\[(\d+)\]', return_type) + if found: + return_type, size = found.groups() + lines.append(f'typedef struct {{ {_get_c_type(return_type)} x[{size}]; }} {route_name}_t;') else: lines.append(f'typedef {_get_c_type(return_type)} {route_name}_t;') diff --git a/quantum/xap/xap_handlers.c b/quantum/xap/xap_handlers.c index 8d0b2e472509..c6350b103861 100644 --- a/quantum/xap/xap_handlers.c +++ b/quantum/xap/xap_handlers.c @@ -17,6 +17,7 @@ #include #include +#include "hardware_id.h" #include "secure.h" #ifndef SECURE_ENABLE # define secure_get_status() SECURE_UNLOCKED @@ -98,6 +99,11 @@ bool xap_respond_request_bootloader_jump(xap_token_t token, const void *data, si } #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)); +} + #if ((defined(DYNAMIC_KEYMAP_ENABLE))) bool xap_respond_dynamic_keymap_get_keycode(xap_token_t token, const void *data, size_t length) { if (length != sizeof(xap_route_dynamic_keymap_get_keymap_keycode_arg_t)) { From 3730ddacacc5e12567a5947577b9db8691306d98 Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 19 Apr 2022 02:06:01 +0100 Subject: [PATCH 060/203] Fix ARM builds due to packing inconsistencies --- .../qmk/xap/gen_firmware/header_generator.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/python/qmk/xap/gen_firmware/header_generator.py b/lib/python/qmk/xap/gen_firmware/header_generator.py index 292cd4a7b3d4..52b1bc29cada 100755 --- a/lib/python/qmk/xap/gen_firmware/header_generator.py +++ b/lib/python/qmk/xap/gen_firmware/header_generator.py @@ -131,11 +131,16 @@ def _append_route_types(lines, container, container_id=None, route_stack=None): member_type = _get_c_type(member['type']) member_name = to_snake(member['name']) lines.append(f' {member_type} {member_name};') - lines.append(f'}} {route_name}_arg_t;') + lines.append(f'}} __attribute__((__packed__)) {route_name}_arg_t;') elif 'request_type' in container: request_type = container['request_type'] - lines.append(f'typedef {_get_c_type(request_type)} {route_name}_arg_t;') + 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 '' @@ -146,14 +151,14 @@ def _append_route_types(lines, container, container_id=None, route_stack=None): 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'}} {route_name}_t;') + lines.append(f'}} __attribute__((__packed__)) {route_name}_t;') 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 {{ {_get_c_type(return_type)} x[{size}]; }} {route_name}_t;') + 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;') @@ -207,7 +212,7 @@ def _append_internal_types(lines, container): if member_type == 'unknown': member_type = additional_types[member["type"]] lines.append(f' {member_type} {member_name};') - lines.append(f'}} xap_{key}_t;') + lines.append(f'}} __attribute__((__packed__)) xap_{key}_t;') else: lines.append(f'typedef {data_type} xap_{key}_t;') From d17aed8e8266398113b50e7a7cc6abbd46d205fb Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Tue, 19 Apr 2022 12:33:56 +1000 Subject: [PATCH 061/203] Verify struct sizing at build time. --- data/xap/xap_0.0.1.hjson | 2 ++ data/xap/xap_0.1.0.hjson | 2 ++ data/xap/xap_0.2.0.hjson | 2 ++ lib/python/qmk/xap/gen_firmware/header_generator.py | 9 +++++++++ 4 files changed, 15 insertions(+) diff --git a/data/xap/xap_0.0.1.hjson b/data/xap/xap_0.0.1.hjson index b8a479f01b9d..382075b94152 100755 --- a/data/xap/xap_0.0.1.hjson +++ b/data/xap/xap_0.0.1.hjson @@ -142,6 +142,7 @@ name: Request Header description: Packet format for inbound data. type: struct + struct_length: 3 struct_members: [ { type: token @@ -158,6 +159,7 @@ name: Response Header description: Packet format for inbound data. type: struct + struct_length: 4 struct_members: [ { type: token diff --git a/data/xap/xap_0.1.0.hjson b/data/xap/xap_0.1.0.hjson index 7ad69aafba4b..9499314d4bf8 100755 --- a/data/xap/xap_0.1.0.hjson +++ b/data/xap/xap_0.1.0.hjson @@ -83,6 +83,7 @@ name: Broadcast Header description: Packet format for broadcast messages. type: struct + struct_length: 4 struct_members: [ { type: token @@ -240,6 +241,7 @@ Retrieves the set of identifying information for the board. ''' return_type: struct + return_struct_length: 10 return_struct_members: [ { type: u16 diff --git a/data/xap/xap_0.2.0.hjson b/data/xap/xap_0.2.0.hjson index f2fce07b844d..2cb71035789a 100755 --- a/data/xap/xap_0.2.0.hjson +++ b/data/xap/xap_0.2.0.hjson @@ -38,6 +38,7 @@ define: GET_KEYMAP_KEYCODE description: TODO request_type: struct + request_struct_length: 3 request_struct_members: [ { type: u8 @@ -61,6 +62,7 @@ define: SET_KEYMAP_KEYCODE description: TODO request_type: struct + request_struct_length: 5 request_struct_members: [ { type: u8 diff --git a/lib/python/qmk/xap/gen_firmware/header_generator.py b/lib/python/qmk/xap/gen_firmware/header_generator.py index 52b1bc29cada..7ce1d9d84ed1 100755 --- a/lib/python/qmk/xap/gen_firmware/header_generator.py +++ b/lib/python/qmk/xap/gen_firmware/header_generator.py @@ -133,6 +133,9 @@ def _append_route_types(lines, container, container_id=None, route_stack=None): 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) @@ -153,6 +156,9 @@ def _append_route_types(lines, container, container_id=None, route_stack=None): 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) @@ -213,6 +219,9 @@ def _append_internal_types(lines, container): 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;') From 4d895892e5349285f3eb9c86c35e723e629f048c Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 20 Apr 2022 22:38:06 +0100 Subject: [PATCH 062/203] Stubs for ENCODER_MAP --- data/xap/xap_0.2.0.hjson | 52 ++++++++++++++++++++++++++++++++++- lib/python/qmk/cli/xap/xap.py | 37 ++++++++++++++++--------- quantum/xap/xap_handlers.c | 25 +++++++++++++++++ 3 files changed, 100 insertions(+), 14 deletions(-) diff --git a/data/xap/xap_0.2.0.hjson b/data/xap/xap_0.2.0.hjson index 2cb71035789a..6fd985314827 100755 --- a/data/xap/xap_0.2.0.hjson +++ b/data/xap/xap_0.2.0.hjson @@ -81,7 +81,6 @@ name: Keycode } ] - return_type: u8 return_execute: dynamic_keymap_set_keycode } } @@ -109,6 +108,57 @@ return_purpose: capabilities return_constant: XAP_ROUTE_DYNAMIC_ENCODER_CAPABILITIES } + 0x02: { + type: command + name: Get Keycode + define: GET_ENCODER_KEYCODE + description: TODO + 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: dynamic_encoder_get_keycode + } + 0x03: { + type: command + name: Set Keycode + define: SET_ENCODER_KEYCODE + description: TODO + 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 + } } } diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index 71ff23a9a4a8..8ce77e422a90 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -157,26 +157,34 @@ def _list_devices(): print_dotted_output(data) -def xap_doit(): - print("xap_doit") +def xap_dump_keymap(device): # get layer count - # layers = _xap_transaction(device, 0x04, 0x01) - # layers = int.from_bytes(layers, "little") - # print(f'layers:{layers}') + layers = _xap_transaction(device, 0x04, 0x01) + layers = int.from_bytes(layers, "little") + print(f'layers:{layers}') # get keycode [layer:0, row:0, col:0] # keycode = _xap_transaction(device, 0x04, 0x02, b"\x00\x00\x00") - # keycode = int.from_bytes(keycode, "little") - # keycode_map = { - # # TODO: this should be data driven... - # 0x04: 'KC_A', - # 0x05: 'KC_B', - # 0x29: 'KC_ESCAPE' - # } - # print('keycode:' + keycode_map.get(keycode, 'unknown')) + # get encoder [layer:0, index:0, clockwise:0] + keycode = _xap_transaction(device, 0x05, 0x02, b"\x00\x00\x00") + + keycode = int.from_bytes(keycode, "little") + keycode_map = { + # TODO: this should be data driven... + 0x04: 'KC_A', + 0x05: 'KC_B', + 0x29: 'KC_ESCAPE', + 0xF9: 'KC_MS_WH_UP', + } + print(f'keycode:{keycode_map.get(keycode, "unknown")}') + + +def xap_doit(): + print("xap_doit") # Reboot # _xap_transaction(device, 0x01, 0x07) + exit(1) def xap_broadcast_listen(device): @@ -227,6 +235,9 @@ def xap(cli): xap_unlock(device) cli.log.info("Done") + elif cli.args.action == 'dump': + xap_dump_keymap(device) + elif cli.args.action == 'listen': xap_broadcast_listen(device) diff --git a/quantum/xap/xap_handlers.c b/quantum/xap/xap_handlers.c index c6350b103861..52b74cc2b0b6 100644 --- a/quantum/xap/xap_handlers.c +++ b/quantum/xap/xap_handlers.c @@ -128,3 +128,28 @@ bool xap_respond_dynamic_keymap_set_keycode(xap_token_t token, const void *data, return true; } #endif + +#if ((defined(ENCODER_MAP_ENABLE))) +bool xap_respond_dynamic_encoder_get_keycode(xap_token_t token, const void *data, size_t length) { + if (length != sizeof(xap_route_dynamic_encoder_get_encoder_keycode_arg_t)) { + return false; + } + + xap_route_dynamic_encoder_get_encoder_keycode_arg_t *arg = (xap_route_dynamic_encoder_get_encoder_keycode_arg_t *)data; + + uint16_t keycode = dynamic_keymap_get_encoder(arg->layer, arg->encoder, arg->clockwise); + return xap_respond_data(token, &keycode, sizeof(keycode)); +} + +bool xap_respond_dynamic_encoder_set_keycode(xap_token_t token, const void *data, size_t length) { + if (length != sizeof(xap_route_dynamic_encoder_set_encoder_keycode_arg_t)) { + return false; + } + + xap_route_dynamic_encoder_set_encoder_keycode_arg_t *arg = (xap_route_dynamic_encoder_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 \ No newline at end of file From c01e8ed75d8a8624c64fcf283218446ceaa5feb7 Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 20 Apr 2022 23:47:08 +0100 Subject: [PATCH 063/203] stash --- lib/python/qmk/cli/xap/xap.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index 8ce77e422a90..737d9738b1ae 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -177,7 +177,10 @@ def xap_dump_keymap(device): 0x29: 'KC_ESCAPE', 0xF9: 'KC_MS_WH_UP', } - print(f'keycode:{keycode_map.get(keycode, "unknown")}') + print(f'keycode:{keycode_map.get(keycode, "unknown")}[{keycode}]') + + # set encoder [layer:0, index:0, clockwise:0, keycode:KC_A] + _xap_transaction(device, 0x05, 0x03, b"\x00\x00\x00\x04\00") def xap_doit(): From 01cd1ac71f6a9b9199e9c9212ae11c03a89826ca Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 21 Apr 2022 18:26:27 +0100 Subject: [PATCH 064/203] stash --- keyboards/zvecr/zv48/keymaps/xap/keymap.c | 102 ++++++++++++++++++++++ keyboards/zvecr/zv48/keymaps/xap/rules.mk | 2 + 2 files changed, 104 insertions(+) create mode 100644 keyboards/zvecr/zv48/keymaps/xap/keymap.c create mode 100644 keyboards/zvecr/zv48/keymaps/xap/rules.mk diff --git a/keyboards/zvecr/zv48/keymaps/xap/keymap.c b/keyboards/zvecr/zv48/keymaps/xap/keymap.c new file mode 100644 index 000000000000..c45e573bd93f --- /dev/null +++ b/keyboards/zvecr/zv48/keymaps/xap/keymap.c @@ -0,0 +1,102 @@ +// Copyright 2020 zvecr +// 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 diff --git a/keyboards/zvecr/zv48/keymaps/xap/rules.mk b/keyboards/zvecr/zv48/keymaps/xap/rules.mk new file mode 100644 index 000000000000..1eb54897cdfd --- /dev/null +++ b/keyboards/zvecr/zv48/keymaps/xap/rules.mk @@ -0,0 +1,2 @@ +ENCODER_MAP_ENABLE = yes +XAP_ENABLE = yes From c65ec90484edfbddf4c5ba824066717e5d39fde3 Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 5 May 2022 21:05:10 +0100 Subject: [PATCH 065/203] Fix a few mistakes in docs --- data/xap/xap_0.0.1.hjson | 4 ++-- docs/xap_0.0.1.md | 4 ++-- docs/xap_0.1.0.md | 5 +++-- docs/xap_0.2.0.md | 5 +++-- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/data/xap/xap_0.0.1.hjson b/data/xap/xap_0.0.1.hjson index 382075b94152..48325247bf69 100755 --- a/data/xap/xap_0.0.1.hjson +++ b/data/xap/xap_0.0.1.hjson @@ -46,7 +46,7 @@ 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, allowing repsonse 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. + Each request needs to include a _token_ -- this `u16` value prefixes each outbound request from the host application and its corresponding response, allowing 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. This token is followed by a `u8` signifying the length of data in the request. ''' @@ -157,7 +157,7 @@ response_header: { name: Response Header - description: Packet format for inbound data. + description: Packet format for outbound data. type: struct struct_length: 4 struct_members: [ diff --git a/docs/xap_0.0.1.md b/docs/xap_0.0.1.md index afa23c85c367..313ab9e488a1 100644 --- a/docs/xap_0.0.1.md +++ b/docs/xap_0.0.1.md @@ -27,14 +27,14 @@ This list defines the terms used across the entire set of XAP protocol documenta | _ID_ | A single octet / 8-bit byte, representing Subsystem or Route index. | | _Request Header_ | Packet format for inbound data. Takes the format:
`token` - token
`u8` - length | | _Response Flags_ | An `u8` containing the status of the request. | -| _Response Header_ | Packet format for inbound data. Takes the format:
`token` - token
`response_flags` - flags
`u8` - length | +| _Response Header_ | Packet format for outbound data. Takes the format:
`token` - token
`response_flags` - flags
`u8` - length | | _Token_ | A `u16` associated with a specific request as well as its corresponding 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, allowing repsonse 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. +Each request needs to include a _token_ -- this `u16` value prefixes each outbound request from the host application and its corresponding response, allowing 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. This token is followed by a `u8` signifying the length of data in the request. diff --git a/docs/xap_0.1.0.md b/docs/xap_0.1.0.md index c61ee1df6037..73a76680c573 100644 --- a/docs/xap_0.1.0.md +++ b/docs/xap_0.1.0.md @@ -29,17 +29,18 @@ This list defines the terms used across the entire set of XAP protocol documenta | _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:
`token` - token
`u8` - type
`u8` - length | | _ID_ | A single octet / 8-bit byte, representing Subsystem or Route index. | | _Request Header_ | Packet format for inbound data. Takes the format:
`token` - token
`u8` - length | | _Response Flags_ | An `u8` containing the status of the request. | -| _Response Header_ | Packet format for inbound data. Takes the format:
`token` - token
`response_flags` - flags
`u8` - length | +| _Response Header_ | Packet format for outbound data. Takes the format:
`token` - token
`response_flags` - flags
`u8` - length | | _Token_ | A `u16` associated with a specific request as well as its corresponding 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, allowing repsonse 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. +Each request needs to include a _token_ -- this `u16` value prefixes each outbound request from the host application and its corresponding response, allowing 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. This token is followed by a `u8` signifying the length of data in the request. diff --git a/docs/xap_0.2.0.md b/docs/xap_0.2.0.md index c61ee1df6037..73a76680c573 100644 --- a/docs/xap_0.2.0.md +++ b/docs/xap_0.2.0.md @@ -29,17 +29,18 @@ This list defines the terms used across the entire set of XAP protocol documenta | _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:
`token` - token
`u8` - type
`u8` - length | | _ID_ | A single octet / 8-bit byte, representing Subsystem or Route index. | | _Request Header_ | Packet format for inbound data. Takes the format:
`token` - token
`u8` - length | | _Response Flags_ | An `u8` containing the status of the request. | -| _Response Header_ | Packet format for inbound data. Takes the format:
`token` - token
`response_flags` - flags
`u8` - length | +| _Response Header_ | Packet format for outbound data. Takes the format:
`token` - token
`response_flags` - flags
`u8` - length | | _Token_ | A `u16` associated with a specific request as well as its corresponding 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, allowing repsonse 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. +Each request needs to include a _token_ -- this `u16` value prefixes each outbound request from the host application and its corresponding response, allowing 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. This token is followed by a `u8` signifying the length of data in the request. From cc851142faf60ad0c75f95fd588a68e8159fec47 Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 5 May 2022 22:16:38 +0100 Subject: [PATCH 066/203] Add cli interactive shell --- lib/python/qmk/cli/xap/xap.py | 102 ++++++++++++++++++++++++---------- 1 file changed, 74 insertions(+), 28 deletions(-) diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index 737d9738b1ae..c49c6062ccc3 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -1,5 +1,6 @@ """Interactions with compatible XAP devices """ +import cmd import json import random import gzip @@ -7,6 +8,14 @@ from milc import cli +KEYCODE_MAP = { + # TODO: this should be data driven... + 0x04: 'KC_A', + 0x05: 'KC_B', + 0x29: 'KC_ESCAPE', + 0xF9: 'KC_MS_WH_UP', +} + def _is_xap_usage(x): return x['usage_page'] == 0xFF51 and x['usage'] == 0x0058 @@ -170,26 +179,12 @@ def xap_dump_keymap(device): keycode = _xap_transaction(device, 0x05, 0x02, b"\x00\x00\x00") keycode = int.from_bytes(keycode, "little") - keycode_map = { - # TODO: this should be data driven... - 0x04: 'KC_A', - 0x05: 'KC_B', - 0x29: 'KC_ESCAPE', - 0xF9: 'KC_MS_WH_UP', - } - print(f'keycode:{keycode_map.get(keycode, "unknown")}[{keycode}]') + print(f'keycode:{KEYCODE_MAP.get(keycode, "unknown")}[{keycode}]') # set encoder [layer:0, index:0, clockwise:0, keycode:KC_A] _xap_transaction(device, 0x05, 0x03, b"\x00\x00\x00\x04\00") -def xap_doit(): - print("xap_doit") - # Reboot - # _xap_transaction(device, 0x01, 0x07) - exit(1) - - def xap_broadcast_listen(device): try: cli.log.info("Listening for XAP broadcasts...") @@ -208,9 +203,67 @@ def xap_unlock(device): _xap_transaction(device, 0x00, 0x04) +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 + + def do_about(self, arg): + """Prints out the current version of QMK with a build date + """ + data = _query_device(self.device) + print(data) + + def do_unlock(self, arg): + """Initiate secure unlock + """ + xap_unlock(self.device) + print("Done") + + def do_listen(self, arg): + """Log out XAP broadcast messages + """ + xap_broadcast_listen(self.device) + + 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 = _xap_transaction(self.device, 0x04, 0x02, data) + keycode = int.from_bytes(keycode, "little") + print(f'keycode:{KEYCODE_MAP.get(keycode, "unknown")}[{keycode}]') + + 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 :.') -@cli.argument('-i', '--index', default=0, help='device index to select.') @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='?', arg_only=True) @cli.subcommand('Acquire debugging information from usb XAP devices.', hidden=False if cli.config.user.developer else True) def xap(cli): @@ -233,16 +286,9 @@ def xap(cli): device = hid.Device(path=dev['path']) cli.log.info("Connected to:%04x:%04x %s %s", dev['vendor_id'], dev['product_id'], dev['manufacturer_string'], dev['product_string']) - # xap_doit(device) - if cli.args.action == 'unlock': - xap_unlock(device) - cli.log.info("Done") - - elif cli.args.action == 'dump': - xap_dump_keymap(device) - - elif cli.args.action == 'listen': - xap_broadcast_listen(device) + # shell? + if cli.args.interactive: + XAPShell(device).loop() + return True - elif not cli.args.action: - xap_broadcast_listen(device) + XAPShell(device).onecmd(cli.args.action or 'listen') From 94ec23ea77c1e3a54b8c61b8c85d7561d8416b8b Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 5 May 2022 22:35:04 +0100 Subject: [PATCH 067/203] Remove requirement to quote action args --- lib/python/qmk/cli/xap/xap.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index c49c6062ccc3..a04970f60854 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -264,7 +264,7 @@ def loop(self): @cli.argument('-d', '--device', help='device to select - uses format :.') @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='?', arg_only=True) +@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 @@ -291,4 +291,4 @@ def xap(cli): XAPShell(device).loop() return True - XAPShell(device).onecmd(cli.args.action or 'listen') + XAPShell(device).onecmd(" ".join(cli.args.action)) From 58642ff40cb8a9956e28da7ab1be9ee5fef26a91 Mon Sep 17 00:00:00 2001 From: zvecr Date: Fri, 6 May 2022 23:11:16 +0100 Subject: [PATCH 068/203] Publish resolved XAP specs? --- lib/python/qmk/cli/generate/api.py | 57 ++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/lib/python/qmk/cli/generate/api.py b/lib/python/qmk/cli/generate/api.py index 0596b3f22b50..d49e40c591c7 100755 --- a/lib/python/qmk/cli/generate/api.py +++ b/lib/python/qmk/cli/generate/api.py @@ -2,6 +2,7 @@ """ from pathlib import Path import shutil +import hjson import json from milc import cli @@ -11,22 +12,47 @@ 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/') +def _filtered_keyboard_list(): + """Perform basic filtering of list_keyboards + """ + keyboard_list = list_keyboards() + if cli.args.filter: + kb_list = [] + for keyboard_name in keyboard_list: + 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, replace specs with pre-merged versions + """ + 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': '-'} + + hjson.dump(overall, (output_folder / file.name).open(mode='w', 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('Creates a new keymap for the keyboard of your choosing', hidden=False if cli.config.user.developer else True) +@cli.subcommand('Generate QMK API data', hidden=False if cli.config.user.developer else True) def generate_api(cli): """Generates the QMK API data. """ - 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 @@ -34,14 +60,14 @@ def generate_api(cli): 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 = list_keyboards() - if cli.args.filter: - kb_list = [] - for keyboard_name in keyboard_list: - if any(i in keyboard_name for i in cli.args.filter): - kb_list.append(keyboard_name) - keyboard_list = kb_list + keyboard_list = _filtered_keyboard_list() kb_all = {} usb_list = {} @@ -86,6 +112,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) From 7e819d794516495587476f130ee1fe2a331ca232 Mon Sep 17 00:00:00 2001 From: zvecr Date: Fri, 6 May 2022 23:33:51 +0100 Subject: [PATCH 069/203] specs as json? --- lib/python/qmk/cli/generate/api.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/python/qmk/cli/generate/api.py b/lib/python/qmk/cli/generate/api.py index d49e40c591c7..3d3c8c9d532a 100755 --- a/lib/python/qmk/cli/generate/api.py +++ b/lib/python/qmk/cli/generate/api.py @@ -33,7 +33,7 @@ def _filtered_keyboard_list(): def _resolve_xap_specs(output_folder): - """To make it easier for consumers, replace specs with pre-merged versions + """To make it easier for consumers, publish pre-merged spec files """ overall = None for file in get_xap_definition_files(): @@ -44,7 +44,8 @@ def _resolve_xap_specs(output_folder): if str(n) not in overall['response_flags']['bits']: overall['response_flags']['bits'][str(n)] = {'name': '', 'description': '', 'define': '-'} - hjson.dump(overall, (output_folder / file.name).open(mode='w', encoding='utf-8')) + 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.") From ea92d5ed7d6641e42698c8c648875c7ed28bcdb6 Mon Sep 17 00:00:00 2001 From: zvecr Date: Mon, 9 May 2022 23:51:43 +0100 Subject: [PATCH 070/203] Block out basic keycodes --- data/constants/keycodes_0.0.1.json | 1298 ++++++++++++++++++++++++++++ data/xap/xap_0.1.0.hjson | 4 + lib/python/qmk/cli/xap/xap.py | 14 +- 3 files changed, 1309 insertions(+), 7 deletions(-) create mode 100644 data/constants/keycodes_0.0.1.json diff --git a/data/constants/keycodes_0.0.1.json b/data/constants/keycodes_0.0.1.json new file mode 100644 index 000000000000..cd3c2876edfe --- /dev/null +++ b/data/constants/keycodes_0.0.1.json @@ -0,0 +1,1298 @@ +{ + "groups": { + "basic": {} + }, + "ranges": { + "0x0000/0x00FF": {} + }, + "keycodes": { + "0x0000": { + "group": "basic", + "key": "KC_NO", + "label": "", + "aliases": [ + "XXXXXXX" + ] + }, + "0x0001": { + "group": "basic", + "key": "KC_TRANSPARENT", + "label": "", + "aliases": [ + "_______", + "KC_TRNS" + ] + }, + "0x0004": { + "group": "basic", + "key": "KC_A", + "label": "A" + }, + "0x0005": { + "group": "basic", + "key": "KC_B", + "label": "B" + }, + "0x0006": { + "group": "basic", + "key": "KC_C", + "label": "C" + }, + "0x0007": { + "group": "basic", + "key": "KC_D", + "label": "D" + }, + "0x0008": { + "group": "basic", + "key": "KC_E", + "label": "E" + }, + "0x0009": { + "group": "basic", + "key": "KC_F", + "label": "F" + }, + "0x000a": { + "group": "basic", + "key": "KC_G", + "label": "G" + }, + "0x000b": { + "group": "basic", + "key": "KC_H", + "label": "H" + }, + "0x000c": { + "group": "basic", + "key": "KC_I", + "label": "I" + }, + "0x000d": { + "group": "basic", + "key": "KC_J", + "label": "J" + }, + "0x000e": { + "group": "basic", + "key": "KC_K", + "label": "K" + }, + "0x000f": { + "group": "basic", + "key": "KC_L", + "label": "L" + }, + "0x0010": { + "group": "basic", + "key": "KC_M", + "label": "M" + }, + "0x0011": { + "group": "basic", + "key": "KC_N", + "label": "N" + }, + "0x0012": { + "group": "basic", + "key": "KC_O", + "label": "O" + }, + "0x0013": { + "group": "basic", + "key": "KC_P", + "label": "P" + }, + "0x0014": { + "group": "basic", + "key": "KC_Q", + "label": "Q" + }, + "0x0015": { + "group": "basic", + "key": "KC_R", + "label": "R" + }, + "0x0016": { + "group": "basic", + "key": "KC_S", + "label": "S" + }, + "0x0017": { + "group": "basic", + "key": "KC_T", + "label": "T" + }, + "0x0018": { + "group": "basic", + "key": "KC_U", + "label": "U" + }, + "0x0019": { + "group": "basic", + "key": "KC_V", + "label": "V" + }, + "0x001a": { + "group": "basic", + "key": "KC_W", + "label": "W" + }, + "0x001b": { + "group": "basic", + "key": "KC_X", + "label": "X" + }, + "0x001c": { + "group": "basic", + "key": "KC_Y", + "label": "Y" + }, + "0x001d": { + "group": "basic", + "key": "KC_Z", + "label": "Z" + }, + "0x001e": { + "group": "basic", + "key": "KC_1", + "label": "1" + }, + "0x001f": { + "group": "basic", + "key": "KC_2", + "label": "2" + }, + "0x0020": { + "group": "basic", + "key": "KC_3", + "label": "3" + }, + "0x0021": { + "group": "basic", + "key": "KC_4", + "label": "4" + }, + "0x0022": { + "group": "basic", + "key": "KC_5", + "label": "5" + }, + "0x0023": { + "group": "basic", + "key": "KC_6", + "label": "6" + }, + "0x0024": { + "group": "basic", + "key": "KC_7", + "label": "7" + }, + "0x0025": { + "group": "basic", + "key": "KC_8", + "label": "8" + }, + "0x0026": { + "group": "basic", + "key": "KC_9", + "label": "9" + }, + "0x0027": { + "group": "basic", + "key": "KC_0", + "label": "0" + }, + "0x0028": { + "group": "basic", + "key": "KC_ENTER", + "label": "0", + "aliases": [ + "KC_ENT" + ] + }, + "0x0029": { + "group": "basic", + "key": "KC_ESCAPE", + "label": "0", + "aliases": [ + "KC_ESC" + ] + }, + "0x002a": { + "group": "basic", + "key": "KC_BACKSPACE", + "label": "0", + "aliases": [ + "KC_BSPC" + ] + }, + "0x002b": { + "group": "basic", + "key": "KC_TAB", + "label": "Tab" + }, + "0x002c": { + "group": "basic", + "key": "KC_SPACE", + "label": "Spacebar", + "aliases": [ + "KC_SPC" + ] + }, + "0x002d": { + "group": "basic", + "key": "KC_MINUS", + "label": "-", + "aliases": [ + "KC_MINS" + ] + }, + "0x002e": { + "group": "basic", + "key": "KC_EQUAL", + "label": "=", + "aliases": [ + "KC_EQL" + ] + }, + "0x002f": { + "group": "basic", + "key": "KC_LEFT_BRACKET", + "label": "]", + "aliases": [ + "KC_LBRC" + ] + }, + "0x0030": { + "group": "basic", + "key": "KC_RIGHT_BRACKET", + "label": "[", + "aliases": [ + "KC_RBRC" + ] + }, + "0x0031": { + "group": "basic", + "key": "KC_BACKSLASH", + "label": "\\", + "aliases": [ + "KC_BSLS" + ] + }, + "0x0032": { + "group": "basic", + "key": "KC_NONUS_HASH", + "label": "#", + "aliases": [ + "KC_NUHS" + ] + }, + "0x0033": { + "group": "basic", + "key": "KC_SEMICOLON", + "label": ";", + "aliases": [ + "KC_SCLN" + ] + }, + "0x0034": { + "group": "basic", + "key": "KC_QUOTE", + "label": "'", + "aliases": [ + "KC_QUOT" + ] + }, + "0x0035": { + "group": "basic", + "key": "KC_GRAVE", + "label": "`", + "aliases": [ + "KC_GRV" + ] + }, + "0x0036": { + "group": "basic", + "key": "KC_COMMA", + "label": ",", + "aliases": [ + "KC_COMM" + ] + }, + "0x0037": { + "group": "basic", + "key": "KC_DOT", + "label": "." + }, + "0x0038": { + "group": "basic", + "key": "KC_SLASH", + "label": "/", + "aliases": [ + "KC_SLSH" + ] + }, + "0x0039": { + "group": "basic", + "key": "KC_CAPS_LOCK", + "label": "Caps Lock", + "aliases": [ + "KC_CAPS" + ] + }, + "0x003a": { + "group": "basic", + "key": "KC_F1", + "label": "F1" + }, + "0x003b": { + "group": "basic", + "key": "KC_F2", + "label": "F2" + }, + "0x003c": { + "group": "basic", + "key": "KC_F3", + "label": "F3" + }, + "0x003d": { + "group": "basic", + "key": "KC_F4", + "label": "F4" + }, + "0x003e": { + "group": "basic", + "key": "KC_F5", + "label": "F5" + }, + "0x003f": { + "group": "basic", + "key": "KC_F6", + "label": "F6" + }, + "0x0040": { + "group": "basic", + "key": "KC_F7", + "label": "F7" + }, + "0x0041": { + "group": "basic", + "key": "KC_F8", + "label": "F8" + }, + "0x0042": { + "group": "basic", + "key": "KC_F9", + "label": "F9" + }, + "0x0043": { + "group": "basic", + "key": "KC_F10", + "label": "F10" + }, + "0x0044": { + "group": "basic", + "key": "KC_F11", + "label": "F11" + }, + "0x0045": { + "group": "basic", + "key": "KC_F12", + "label": "F12" + }, + "0x0046": { + "group": "basic", + "key": "KC_PRINT_SCREEN", + "label": "Print Screen", + "aliases": [ + "KC_PSCR" + ] + }, + "0x0047": { + "group": "basic", + "key": "KC_SCROLL_LOCK", + "label": "Scroll Lock", + "aliases": [ + "KC_SCRL" + ] + }, + "0x0048": { + "group": "basic", + "key": "KC_PAUSE", + "label": "Pause", + "aliases": [ + "KC_PAUS", + "KC_BRK" + ] + }, + "0x0049": { + "group": "basic", + "key": "KC_INSERT", + "label": "Insert", + "aliases": [ + "KC_INS" + ] + }, + "0x004a": { + "group": "basic", + "key": "KC_HOME", + "label": "Home" + }, + "0x004b": { + "group": "basic", + "key": "KC_PAGE_UP", + "label": "Page Up", + "aliases": [ + "KC_PGUP" + ] + }, + "0x004c": { + "group": "basic", + "key": "KC_DELETE", + "label": "Delete", + "aliases": [ + "KC_DEL" + ] + }, + "0x004d": { + "group": "basic", + "key": "KC_END", + "label": "End" + }, + "0x004e": { + "group": "basic", + "key": "KC_PAGE_DOWN", + "label": "Page Down", + "aliases": [ + "KC_PGDN" + ] + }, + "0x004f": { + "group": "basic", + "key": "KC_RIGHT", + "label": "Right", + "aliases": [ + "KC_RGHT" + ] + }, + "0x0050": { + "group": "basic", + "key": "KC_LEFT", + "label": "Left" + }, + "0x0051": { + "group": "basic", + "key": "KC_DOWN", + "label": "Down" + }, + "0x0052": { + "group": "basic", + "key": "KC_Up", + "label": "Up" + }, + "0x0053": { + "group": "basic", + "key": "KC_NUM_LOCK", + "label": "Num Lock", + "aliases": [ + "KC_NUM" + ] + }, + "0x0054": { + "group": "basic", + "key": "KC_KP_SLASH", + "label": "/", + "aliases": [ + "KC_PSLS" + ] + }, + "0x0055": { + "group": "basic", + "key": "KC_KP_ASTERISK", + "label": "*", + "aliases": [ + "KC_PAST" + ] + }, + "0x0056": { + "group": "basic", + "key": "KC_KP_MINUS", + "label": "-", + "aliases": [ + "KC_PMNS" + ] + }, + "0x0057": { + "group": "basic", + "key": "KC_KP_PLUS", + "label": "+", + "aliases": [ + "KC_PPLS" + ] + }, + "0x0058": { + "group": "basic", + "key": "KC_KP_ENTER", + "label": "Enter", + "aliases": [ + "KC_PENT" + ] + }, + "0x0059": { + "group": "basic", + "key": "KC_KP_1", + "label": "1", + "aliases": [ + "KC_P1" + ] + }, + "0x005a": { + "group": "basic", + "key": "KC_KP_2", + "label": "2", + "aliases": [ + "KC_P2" + ] + }, + "0x005b": { + "group": "basic", + "key": "KC_KP_3", + "label": "3", + "aliases": [ + "KC_P3" + ] + }, + "0x005c": { + "group": "basic", + "key": "KC_KP_4", + "label": "4", + "aliases": [ + "KC_P4" + ] + }, + "0x005d": { + "group": "basic", + "key": "KC_KP_5", + "label": "5", + "aliases": [ + "KC_P5" + ] + }, + "0x005e": { + "group": "basic", + "key": "KC_KP_6", + "label": "6", + "aliases": [ + "KC_P6" + ] + }, + "0x005f": { + "group": "basic", + "key": "KC_KP_7", + "label": "7", + "aliases": [ + "KC_P7" + ] + }, + "0x0060": { + "group": "basic", + "key": "KC_KP_8", + "label": "8", + "aliases": [ + "KC_P8" + ] + }, + "0x0061": { + "group": "basic", + "key": "KC_KP_9", + "label": "9", + "aliases": [ + "KC_P9" + ] + }, + "0x0062": { + "group": "basic", + "key": "KC_KP_0", + "label": "0", + "aliases": [ + "KC_P0" + ] + }, + "0x0063": { + "group": "basic", + "key": "KC_KP_DOT", + "label": ".", + "aliases": [ + "KC_PDOT" + ] + }, + "0x0064": { + "group": "basic", + "key": "KC_NONUS_BACKSLASH", + "label": "\\", + "aliases": [ + "KC_NUBS" + ] + }, + "0x0065": { + "group": "basic", + "key": "KC_APPLICATION", + "label": "Application", + "aliases": [ + "KC_APP" + ] + }, + "0x0066": { + "group": "basic", + "key": "KC_KB_POWER", + "label": "Application" + }, + "0x0067": { + "group": "basic", + "key": "KC_KP_EQUAL", + "label": "=", + "aliases": [ + "KC_PEQL" + ] + }, + "0x0068": { + "group": "basic", + "key": "KC_F13", + "label": "F13" + }, + "0x0069": { + "group": "basic", + "key": "KC_F14", + "label": "F14" + }, + "0x006a": { + "group": "basic", + "key": "KC_F15", + "label": "F15" + }, + "0x006b": { + "group": "basic", + "key": "KC_F16", + "label": "F16" + }, + "0x006c": { + "group": "basic", + "key": "KC_F17", + "label": "F17" + }, + "0x006d": { + "group": "basic", + "key": "KC_F17", + "label": "F18" + }, + "0x006e": { + "group": "basic", + "key": "KC_F19", + "label": "F19" + }, + "0x006f": { + "group": "basic", + "key": "KC_F20", + "label": "F20" + }, + "0x0070": { + "group": "basic", + "key": "KC_F21", + "label": "F21" + }, + "0x0071": { + "group": "basic", + "key": "KC_F22", + "label": "F22" + }, + "0x0072": { + "group": "basic", + "key": "KC_F23", + "label": "F23" + }, + "0x0073": { + "group": "basic", + "key": "KC_F24", + "label": "F24" + }, + "0x0074": { + "group": "basic", + "key": "KC_EXECUTE", + "label": "Execute", + "aliases": [ + "KC_EXEC" + ] + }, + "0x0075": { + "group": "basic", + "key": "KC_HELP", + "label": "Help" + }, + "0x0076": { + "group": "basic", + "key": "KC_MENU", + "label": "Menu" + }, + "0x0077": { + "group": "basic", + "key": "KC_SELECT", + "label": "Select", + "aliases": [ + "KC_SLCT" + ] + }, + "0x0078": { + "group": "basic", + "key": "KC_STOP", + "label": "Stop" + }, + "0x0079": { + "group": "basic", + "key": "KC_AGAIN", + "label": "Again", + "aliases": [ + "KC_AGIN" + ] + }, + "0x007a": { + "group": "basic", + "key": "KC_UNDO", + "label": "Undo" + }, + "0x007b": { + "group": "basic", + "key": "KC_CUT", + "label": "Cut" + }, + "0x007c": { + "group": "basic", + "key": "KC_COPY", + "label": "Copy" + }, + "0x007d": { + "group": "basic", + "key": "KC_PASTE", + "label": "Paste", + "aliases": [ + "KC_PSTE" + ] + }, + "0x007e": { + "group": "basic", + "key": "KC_FIND", + "label": "Find" + }, + "0x007f": { + "group": "basic", + "key": "KC_KB_MUTE", + "label": "Mute" + }, + "0x0080": { + "group": "basic", + "key": "KC_KB_VOLUME_UP", + "label": "Volume Up" + }, + "0x0081": { + "group": "basic", + "key": "KC_KB_VOLUME_DOWN", + "label": "Volume Down" + }, + "0x0082": { + "group": "basic", + "key": "KC_LOCKING_CAPS_LOCK", + "label": "Caps Lock", + "aliases": [ + "KC_LCAP" + ] + }, + "0x0083": { + "group": "basic", + "key": "KC_LOCKING_NUM_LOCK", + "label": "Num Lock", + "aliases": [ + "KC_LSCR" + ] + }, + "0x0084": { + "group": "basic", + "key": "KC_LOCKING_SCROLL_LOCK", + "label": "Scroll Lock", + "aliases": [ + "KC_LCAP" + ] + }, + "0x0085": { + "group": "basic", + "key": "KC_KP_COMMA", + "label": ",", + "aliases": [ + "KC_PCMM" + ] + }, + "0x0086": { + "group": "basic", + "key": "KC_KP_EQUAL_AS400", + "label": "=" + }, + "0x0087": { + "group": "basic", + "key": "KC_INTERNATIONAL_1", + "label": "INT 1", + "aliases": [ + "KC_INT1" + ] + }, + "0x0088": { + "group": "basic", + "key": "KC_INTERNATIONAL_2", + "label": "INT 2", + "aliases": [ + "KC_INT2" + ] + }, + "0x0089": { + "group": "basic", + "key": "KC_INTERNATIONAL_3", + "label": "INT 3", + "aliases": [ + "KC_INT3" + ] + }, + "0x008a": { + "group": "basic", + "key": "KC_INTERNATIONAL_4", + "label": "INT 4", + "aliases": [ + "KC_INT4" + ] + }, + "0x008b": { + "group": "basic", + "key": "KC_INTERNATIONAL_5", + "label": "INT 5", + "aliases": [ + "KC_INT5" + ] + }, + "0x008c": { + "group": "basic", + "key": "KC_INTERNATIONAL_6", + "label": "INT 6", + "aliases": [ + "KC_INT6" + ] + }, + "0x008d": { + "group": "basic", + "key": "KC_INTERNATIONAL_7", + "label": "INT 7", + "aliases": [ + "KC_INT7" + ] + }, + "0x008e": { + "group": "basic", + "key": "KC_INTERNATIONAL_8", + "label": "INT 8", + "aliases": [ + "KC_INT8" + ] + }, + "0x008f": { + "group": "basic", + "key": "KC_INTERNATIONAL_9", + "label": "INT 9", + "aliases": [ + "KC_INT9" + ] + }, + "0x0090": { + "group": "basic", + "key": "KC_LANGUAGE_1", + "label": "LANG 1", + "aliases": [ + "KC_LNG1" + ] + }, + "0x0091": { + "group": "basic", + "key": "KC_LANGUAGE_2", + "label": "LANG 2", + "aliases": [ + "KC_LNG2" + ] + }, + "0x0092": { + "group": "basic", + "key": "KC_LANGUAGE_3", + "label": "LANG 3", + "aliases": [ + "KC_LNG3" + ] + }, + "0x0093": { + "group": "basic", + "key": "KC_LANGUAGE_4", + "label": "LANG 4", + "aliases": [ + "KC_LNG4" + ] + }, + "0x0094": { + "group": "basic", + "key": "KC_LANGUAGE_5", + "label": "LANG 5", + "aliases": [ + "KC_LNG5" + ] + }, + "0x0095": { + "group": "basic", + "key": "KC_LANGUAGE_6", + "label": "LANG 6", + "aliases": [ + "KC_LNG6" + ] + }, + "0x0096": { + "group": "basic", + "key": "KC_LANGUAGE_7", + "label": "LANG 7", + "aliases": [ + "KC_LNG7" + ] + }, + "0x0097": { + "group": "basic", + "key": "KC_LANGUAGE_8", + "label": "LANG 8", + "aliases": [ + "KC_LNG8" + ] + }, + "0x0098": { + "group": "basic", + "key": "KC_LANGUAGE_9", + "label": "LANG 9", + "aliases": [ + "KC_LNG9" + ] + }, + "0x0099": { + "group": "basic", + "key": "KC_ALTERNATE_ERASE", + "label": "Alternate Erase", + "aliases": [ + "KC_ERAS" + ] + }, + "0x009a": { + "group": "basic", + "key": "KC_SYSTEM_REQUEST", + "label": "SysReq/Attention", + "aliases": [ + "KC_SYRQ" + ] + }, + "0x009b": { + "group": "basic", + "key": "KC_CANCEL", + "label": "Cancel", + "aliases": [ + "KC_CNCL" + ] + }, + "0x009c": { + "group": "basic", + "key": "KC_CLEAR", + "label": "Clear", + "aliases": [ + "KC_CLR" + ] + }, + "0x009d": { + "group": "basic", + "key": "KC_PRIOR", + "label": "Prior", + "aliases": [ + "KC_PRIR" + ] + }, + "0x009e": { + "group": "basic", + "key": "KC_RETURN", + "label": "Return", + "aliases": [ + "KC_RETN" + ] + }, + "0x009f": { + "group": "basic", + "key": "KC_SEPARATOR", + "label": "Separator", + "aliases": [ + "KC_SEPR" + ] + }, + "0x00a0": { + "group": "basic", + "key": "KC_OUT", + "label": "Out" + }, + "0x00a1": { + "group": "basic", + "key": "KC_OPER", + "label": "Oper" + }, + "0x00a2": { + "group": "basic", + "key": "KC_CLEAR_AGAIN", + "label": "Clear/Again", + "aliases": [ + "KC_CLAG" + ] + }, + "0x00a3": { + "group": "basic", + "key": "KC_CRSEL", + "label": "CrSel/Props", + "aliases": [ + "KC_CRSL" + ] + }, + "0x00a4": { + "group": "basic", + "key": "KC_EXSEL", + "label": "ExSel", + "aliases": [ + "KC_EXSL" + ] + }, + + "0x00e0": { + "group": "basic", + "key": "KC_LEFT_CTRL", + "label": "Left Control", + "aliases": [ + "KC_LCTL" + ] + }, + "0x00e1": { + "group": "basic", + "key": "KC_LEFT_SHIFT", + "label": "Left Shift", + "aliases": [ + "KC_LSFT" + ] + }, + "0x00e2": { + "group": "basic", + "key": "KC_LEFT_ALT", + "label": "Left Alt", + "aliases": [ + "KC_LALT", + "KC_LOPT" + ] + }, + "0x00e3": { + "group": "basic", + "key": "KC_LEFT_GUI", + "label": "Left GUI", + "aliases": [ + "KC_LGUI", + "KC_LCMD", + "KC_LWIN" + ] + }, + "0x00e4": { + "group": "basic", + "key": "KC_RIGHT_CTRL", + "label": "Right Control", + "aliases": [ + "KC_RCTL" + ] + }, + "0x00e5": { + "group": "basic", + "key": "KC_RIGHT_SHIFT", + "label": "Right Shift", + "aliases": [ + "KC_RSFT" + ] + }, + "0x00e6": { + "group": "basic", + "key": "KC_RIGHT_ALT", + "label": "Right Alt", + "aliases": [ + "KC_RALT", + "KC_ROPT", + "KC_ALGR" + ] + }, + "0x00e7": { + "group": "basic", + "key": "KC_RIGHT_GUI", + "label": "Right GUI", + "aliases": [ + "KC_RGUI", + "KC_RCMD", + "KC_RWIN" + ] + }, + + "0x00ed": { + "group": "basic", + "key": "KC_MS_UP", + "label": "Move cursor up", + "aliases": [ + "KC_MS_U" + ] + }, + "0x00ee": { + "group": "basic", + "key": "KC_MS_DOWN", + "label": "Move cursor down", + "aliases": [ + "KC_MS_D" + ] + }, + "0x00ef": { + "group": "basic", + "key": "KC_MS_LEFT", + "label": "Move cursor left", + "aliases": [ + "KC_MS_L" + ] + }, + "0x00f0": { + "group": "basic", + "key": "KC_MS_LEFT", + "label": "Move cursor right", + "aliases": [ + "KC_MS_R" + ] + }, + "0x00f1": { + "group": "basic", + "key": "KC_MS_BTN1", + "label": "Press button 1", + "aliases": [ + "KC_BTN1" + ] + }, + "0x00f2": { + "group": "basic", + "key": "KC_MS_BTN2", + "label": "Press button 2", + "aliases": [ + "KC_BTN2" + ] + }, + "0x00f3": { + "group": "basic", + "key": "KC_MS_BTN3", + "label": "Press button 3", + "aliases": [ + "KC_BTN3" + ] + }, + "0x00f4": { + "group": "basic", + "key": "KC_MS_BTN4", + "label": "Press button 4", + "aliases": [ + "KC_BTN4" + ] + }, + "0x00f5": { + "group": "basic", + "key": "KC_MS_BTN5", + "label": "Press button 5", + "aliases": [ + "KC_BTN5" + ] + }, + "0x00f6": { + "group": "basic", + "key": "KC_MS_BTN6", + "label": "Press button 6", + "aliases": [ + "KC_BTN6" + ] + }, + "0x00f7": { + "group": "basic", + "key": "KC_MS_BTN7", + "label": "Press button 7", + "aliases": [ + "KC_BTN7" + ] + }, + "0x00f8": { + "group": "basic", + "key": "KC_MS_BTN8", + "label": "Press button 8", + "aliases": [ + "KC_BTN8" + ] + }, + "0x00f9": { + "group": "basic", + "key": "KC_MS_WH_UP", + "label": "Move wheel up", + "aliases": [ + "KC_WH_U" + ] + }, + "0x00fa": { + "group": "basic", + "key": "KC_MS_WH_DOWN", + "label": "Move wheel down", + "aliases": [ + "KC_WH_D" + ] + }, + "0x00fb": { + "group": "basic", + "key": "KC_MS_WH_LEFT", + "label": "Move wheel left", + "aliases": [ + "KC_WH_L" + ] + }, + "0x00fc": { + "group": "basic", + "key": "KC_MS_WH_RIGHT", + "label": "Move wheel right", + "aliases": [ + "KC_WH_R" + ] + }, + "0x00fd": { + "group": "basic", + "key": "KC_MS_ACCEL0", + "label": "Set speed to 0", + "aliases": [ + "KC_ACL0" + ] + }, + "0x00fe": { + "group": "basic", + "key": "KC_MS_ACCEL1", + "label": "Set speed to 1", + "aliases": [ + "KC_ACL1" + ] + }, + "0x00ff": { + "group": "basic", + "key": "KC_MS_ACCEL2", + "label": "Set speed to 2", + "aliases": [ + "KC_ACL2" + ] + } + } +} \ No newline at end of file diff --git a/data/xap/xap_0.1.0.hjson b/data/xap/xap_0.1.0.hjson index 9499314d4bf8..078997a0c541 100755 --- a/data/xap/xap_0.1.0.hjson +++ b/data/xap/xap_0.1.0.hjson @@ -1,6 +1,10 @@ { version: 0.1.0 + uses: { + keycodes: 0.0.1 + } + documentation: { order: [ broadcast_messages diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index a04970f60854..7db2fbcbe355 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -4,17 +4,17 @@ import json import random import gzip +from pathlib import Path from platform import platform from milc import cli -KEYCODE_MAP = { - # TODO: this should be data driven... - 0x04: 'KC_A', - 0x05: 'KC_B', - 0x29: 'KC_ESCAPE', - 0xF9: 'KC_MS_WH_UP', -} +from qmk.json_schema import json_load + +# TODO: get from xap "uses" for the current device +keycode_version = '0.0.1' +spec = json_load(Path(f'data/constants/keycodes_{keycode_version}.json')) +KEYCODE_MAP = {int(k, 16): v.get('key') for k, v in spec['keycodes'].items()} def _is_xap_usage(x): From 41a5dcbfa7ee5dd0a5d5318fdbf697aba067e46e Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 10 May 2022 01:38:14 +0100 Subject: [PATCH 071/203] Add more DD basic keycodes --- data/constants/keycodes_0.0.1.json | 272 +++++++++++++++++++++++++---- lib/python/qmk/cli/xap/xap.py | 25 ++- 2 files changed, 265 insertions(+), 32 deletions(-) diff --git a/data/constants/keycodes_0.0.1.json b/data/constants/keycodes_0.0.1.json index cd3c2876edfe..ea0fd4257d6c 100644 --- a/data/constants/keycodes_0.0.1.json +++ b/data/constants/keycodes_0.0.1.json @@ -1,6 +1,10 @@ { "groups": { - "basic": {} + "basic": {}, + "media": {}, + "modifiers": {}, + "mouse": {} + }, "ranges": { "0x0000/0x00FF": {} @@ -414,7 +418,8 @@ "key": "KC_SCROLL_LOCK", "label": "Scroll Lock", "aliases": [ - "KC_SCRL" + "KC_SCRL", + "KC_BRMD" ] }, "0x0048": { @@ -423,7 +428,8 @@ "label": "Pause", "aliases": [ "KC_PAUS", - "KC_BRK" + "KC_BRK", + "KC_BRMU" ] }, "0x0049": { @@ -1070,8 +1076,214 @@ ] }, + "0x00a5": { + "group": "media", + "key": "KC_SYSTEM_POWER", + "label": "System Power Down", + "aliases": [ + "KC_PWR" + ] + }, + "0x00a6": { + "group": "media", + "key": "KC_SYSTEM_SLEEP", + "label": "System Sleep", + "aliases": [ + "KC_SLEP" + ] + }, + "0x00a7": { + "group": "media", + "key": "KC_SYSTEM_WAKE", + "label": "System Wake", + "aliases": [ + "KC_WAKE" + ] + }, + "0x00a8": { + "group": "media", + "key": "KC_AUDIO_MUTE", + "label": "Mute", + "aliases": [ + "KC_MUTE" + ] + }, + "0x00a9": { + "group": "media", + "key": "KC_AUDIO_VOL_UP", + "label": "Volume Up", + "aliases": [ + "KC_VOLU" + ] + }, + "0x00aa": { + "group": "media", + "key": "KC_AUDIO_VOL_DOWN", + "label": "Volume Down", + "aliases": [ + "KC_VOLD" + ] + }, + "0x00ab": { + "group": "media", + "key": "KC_MEDIA_NEXT_TRACK", + "label": "Next", + "aliases": [ + "KC_MNXT" + ] + }, + "0x00ac": { + "group": "media", + "key": "KC_MEDIA_PREV_TRACK", + "label": "Previous", + "aliases": [ + "KC_MPRV" + ] + }, + "0x00ad": { + "group": "media", + "key": "KC_MEDIA_STOP", + "label": "Stop", + "aliases": [ + "KC_MSTP" + ] + }, + "0x00ae": { + "group": "media", + "key": "KC_MEDIA_PLAY_PAUSE", + "label": "Mute", + "aliases": [ + "KC_MPLY" + ] + }, + "0x00af": { + "group": "media", + "key": "KC_MEDIA_SELECT", + "label": "Launch Player", + "aliases": [ + "KC_MSEL" + ] + }, + "0x00b0": { + "group": "media", + "key": "KC_MEDIA_EJECT", + "label": "Eject", + "aliases": [ + "KC_EJCT" + ] + }, + "0x00b1": { + "group": "media", + "key": "KC_MAIL", + "label": "Launch Mail" + }, + "0x00b2": { + "group": "media", + "key": "KC_CALCULATOR", + "label": "Launch Calculator", + "aliases": [ + "KC_CALC" + ] + }, + "0x00b3": { + "group": "media", + "key": "KC_MY_COMPUTER", + "label": "Launch My Computer", + "aliases": [ + "KC_MYCM" + ] + }, + "0x00b4": { + "group": "media", + "key": "KC_WWW_SEARCH", + "label": "Browser Search", + "aliases": [ + "KC_WSCH" + ] + }, + "0x00b5": { + "group": "media", + "key": "KC_WWW_HOME", + "label": "Browser Home", + "aliases": [ + "KC_WHOM" + ] + }, + "0x00b6": { + "group": "media", + "key": "KC_WWW_BACK", + "label": "Browser Back", + "aliases": [ + "KC_WBAK" + ] + }, + "0x00b7": { + "group": "media", + "key": "KC_WWW_FORWARD", + "label": "Browser Forward", + "aliases": [ + "KC_WFWD" + ] + }, + "0x00b8": { + "group": "media", + "key": "KC_WWW_STOP", + "label": "Browser Stop", + "aliases": [ + "KC_WSTP" + ] + }, + "0x00b9": { + "group": "media", + "key": "KC_WWW_REFRESH", + "label": "Browser Refresh", + "aliases": [ + "KC_WREF" + ] + }, + "0x00ba": { + "group": "media", + "key": "KC_WWW_FAVORITES", + "label": "Browser Favorites", + "aliases": [ + "KC_WREF" + ] + }, + "0x00bb": { + "group": "media", + "key": "KC_MEDIA_FAST_FORWARD", + "label": "Browser Favorites", + "aliases": [ + "KC_WREF" + ] + }, + "0x00bc": { + "group": "media", + "key": "KC_MEDIA_REWIND", + "label": "Browser Favorites", + "aliases": [ + "KC_WFAV" + ] + }, + "0x00bd": { + "group": "media", + "key": "KC_BRIGHTNESS_UP", + "label": "Brightness Up", + "aliases": [ + "KC_BRIU" + ] + }, + "0x00be": { + "group": "media", + "key": "KC_BRIGHTNESS_DOWN", + "label": "Brightness Down", + "aliases": [ + "KC_BRID" + ] + }, + "0x00e0": { - "group": "basic", + "group": "modifiers", "key": "KC_LEFT_CTRL", "label": "Left Control", "aliases": [ @@ -1079,7 +1291,7 @@ ] }, "0x00e1": { - "group": "basic", + "group": "modifiers", "key": "KC_LEFT_SHIFT", "label": "Left Shift", "aliases": [ @@ -1087,7 +1299,7 @@ ] }, "0x00e2": { - "group": "basic", + "group": "modifiers", "key": "KC_LEFT_ALT", "label": "Left Alt", "aliases": [ @@ -1096,7 +1308,7 @@ ] }, "0x00e3": { - "group": "basic", + "group": "modifiers", "key": "KC_LEFT_GUI", "label": "Left GUI", "aliases": [ @@ -1106,7 +1318,7 @@ ] }, "0x00e4": { - "group": "basic", + "group": "modifiers", "key": "KC_RIGHT_CTRL", "label": "Right Control", "aliases": [ @@ -1114,7 +1326,7 @@ ] }, "0x00e5": { - "group": "basic", + "group": "modifiers", "key": "KC_RIGHT_SHIFT", "label": "Right Shift", "aliases": [ @@ -1122,7 +1334,7 @@ ] }, "0x00e6": { - "group": "basic", + "group": "modifiers", "key": "KC_RIGHT_ALT", "label": "Right Alt", "aliases": [ @@ -1132,7 +1344,7 @@ ] }, "0x00e7": { - "group": "basic", + "group": "modifiers", "key": "KC_RIGHT_GUI", "label": "Right GUI", "aliases": [ @@ -1143,7 +1355,7 @@ }, "0x00ed": { - "group": "basic", + "group": "mouse", "key": "KC_MS_UP", "label": "Move cursor up", "aliases": [ @@ -1151,7 +1363,7 @@ ] }, "0x00ee": { - "group": "basic", + "group": "mouse", "key": "KC_MS_DOWN", "label": "Move cursor down", "aliases": [ @@ -1159,7 +1371,7 @@ ] }, "0x00ef": { - "group": "basic", + "group": "mouse", "key": "KC_MS_LEFT", "label": "Move cursor left", "aliases": [ @@ -1167,7 +1379,7 @@ ] }, "0x00f0": { - "group": "basic", + "group": "mouse", "key": "KC_MS_LEFT", "label": "Move cursor right", "aliases": [ @@ -1175,7 +1387,7 @@ ] }, "0x00f1": { - "group": "basic", + "group": "mouse", "key": "KC_MS_BTN1", "label": "Press button 1", "aliases": [ @@ -1183,7 +1395,7 @@ ] }, "0x00f2": { - "group": "basic", + "group": "mouse", "key": "KC_MS_BTN2", "label": "Press button 2", "aliases": [ @@ -1191,7 +1403,7 @@ ] }, "0x00f3": { - "group": "basic", + "group": "mouse", "key": "KC_MS_BTN3", "label": "Press button 3", "aliases": [ @@ -1199,7 +1411,7 @@ ] }, "0x00f4": { - "group": "basic", + "group": "mouse", "key": "KC_MS_BTN4", "label": "Press button 4", "aliases": [ @@ -1207,7 +1419,7 @@ ] }, "0x00f5": { - "group": "basic", + "group": "mouse", "key": "KC_MS_BTN5", "label": "Press button 5", "aliases": [ @@ -1215,7 +1427,7 @@ ] }, "0x00f6": { - "group": "basic", + "group": "mouse", "key": "KC_MS_BTN6", "label": "Press button 6", "aliases": [ @@ -1223,7 +1435,7 @@ ] }, "0x00f7": { - "group": "basic", + "group": "mouse", "key": "KC_MS_BTN7", "label": "Press button 7", "aliases": [ @@ -1231,7 +1443,7 @@ ] }, "0x00f8": { - "group": "basic", + "group": "mouse", "key": "KC_MS_BTN8", "label": "Press button 8", "aliases": [ @@ -1239,7 +1451,7 @@ ] }, "0x00f9": { - "group": "basic", + "group": "mouse", "key": "KC_MS_WH_UP", "label": "Move wheel up", "aliases": [ @@ -1247,7 +1459,7 @@ ] }, "0x00fa": { - "group": "basic", + "group": "mouse", "key": "KC_MS_WH_DOWN", "label": "Move wheel down", "aliases": [ @@ -1255,7 +1467,7 @@ ] }, "0x00fb": { - "group": "basic", + "group": "mouse", "key": "KC_MS_WH_LEFT", "label": "Move wheel left", "aliases": [ @@ -1263,7 +1475,7 @@ ] }, "0x00fc": { - "group": "basic", + "group": "mouse", "key": "KC_MS_WH_RIGHT", "label": "Move wheel right", "aliases": [ @@ -1271,7 +1483,7 @@ ] }, "0x00fd": { - "group": "basic", + "group": "mouse", "key": "KC_MS_ACCEL0", "label": "Set speed to 0", "aliases": [ @@ -1279,7 +1491,7 @@ ] }, "0x00fe": { - "group": "basic", + "group": "mouse", "key": "KC_MS_ACCEL1", "label": "Set speed to 1", "aliases": [ @@ -1287,7 +1499,7 @@ ] }, "0x00ff": { - "group": "basic", + "group": "mouse", "key": "KC_MS_ACCEL2", "label": "Set speed to 2", "aliases": [ diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index 7db2fbcbe355..69ef8e6e7d2a 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -14,7 +14,7 @@ # TODO: get from xap "uses" for the current device keycode_version = '0.0.1' spec = json_load(Path(f'data/constants/keycodes_{keycode_version}.json')) -KEYCODE_MAP = {int(k, 16): v.get('key') for k, v in spec['keycodes'].items()} +KEYCODE_MAP = {int(k, 16): v.get('aliases', [v.get('key')])[0] for k, v in spec['keycodes'].items()} def _is_xap_usage(x): @@ -166,7 +166,7 @@ def _list_devices(): print_dotted_output(data) -def xap_dump_keymap(device): +def xap_dummy(device): # get layer count layers = _xap_transaction(device, 0x04, 0x01) layers = int.from_bytes(layers, "little") @@ -240,6 +240,27 @@ def do_keycode(self, arg): keycode = int.from_bytes(keycode, "little") print(f'keycode:{KEYCODE_MAP.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 = _query_device_info(self.device) + rows = info['matrix_size']['rows'] + cols = info['matrix_size']['cols'] + + # TODO: render like qmk info? + 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 = _xap_transaction(self.device, 0x04, 0x02, q) + keycode = int.from_bytes(keycode, "little") + print(f'| {KEYCODE_MAP.get(keycode, "unknown").ljust(7)} ', end='', flush=True) + print('|') + def do_exit(self, line): """Quit shell """ From 5028d6672af7c43c82e0cecef5cc698b9162a929 Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 10 May 2022 02:29:30 +0100 Subject: [PATCH 072/203] Use keycodes for xap version --- lib/python/qmk/cli/xap/xap.py | 14 ++++++-------- lib/python/qmk/xap/common.py | 31 ++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index 69ef8e6e7d2a..a90625e748ef 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -4,17 +4,13 @@ import json import random import gzip -from pathlib import Path from platform import platform from milc import cli -from qmk.json_schema import json_load +from qmk.xap.common import get_xap_keycodes -# TODO: get from xap "uses" for the current device -keycode_version = '0.0.1' -spec = json_load(Path(f'data/constants/keycodes_{keycode_version}.json')) -KEYCODE_MAP = {int(k, 16): v.get('aliases', [v.get('key')])[0] for k, v in spec['keycodes'].items()} +KEYCODE_MAP = get_xap_keycodes('0.2.0') def _is_xap_usage(x): @@ -210,6 +206,8 @@ class XAPShell(cmd.Cmd): def __init__(self, device): cmd.Cmd.__init__(self) self.device = device + # cache keycodes for this device + self.keycodes = get_xap_keycodes(_query_device(device)['xap']) def do_about(self, arg): """Prints out the current version of QMK with a build date @@ -238,7 +236,7 @@ def do_keycode(self, arg): keycode = _xap_transaction(self.device, 0x04, 0x02, data) keycode = int.from_bytes(keycode, "little") - print(f'keycode:{KEYCODE_MAP.get(keycode, "unknown")}[{keycode}]') + print(f'keycode:{self.keycodes.get(keycode, "unknown")}[{keycode}]') def do_keymap(self, arg): """Prints out the keycode values of a certain layer @@ -258,7 +256,7 @@ def do_keymap(self, arg): q = data + r.to_bytes(1, byteorder='little') + c.to_bytes(1, byteorder='little') keycode = _xap_transaction(self.device, 0x04, 0x02, q) keycode = int.from_bytes(keycode, "little") - print(f'| {KEYCODE_MAP.get(keycode, "unknown").ljust(7)} ', end='', flush=True) + print(f'| {self.keycodes.get(keycode, "unknown").ljust(7)} ', end='', flush=True) print('|') def do_exit(self, line): diff --git a/lib/python/qmk/xap/common.py b/lib/python/qmk/xap/common.py index d1e0d5dbb034..1a39682e099b 100755 --- a/lib/python/qmk/xap/common.py +++ b/lib/python/qmk/xap/common.py @@ -2,9 +2,12 @@ """ import os import hjson +from pathlib import Path +from typing import OrderedDict from jinja2 import Environment, FileSystemLoader, select_autoescape + from qmk.constants import QMK_FIRMWARE -from typing import OrderedDict +from qmk.json_schema import json_load def _get_jinja2_env(data_templates_xap_subdir: str): @@ -78,6 +81,32 @@ def latest_xap_defs(): return _merge_ordered_dicts(definitions) +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 + 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 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. """ From f9f0d84eb0eeb1653ef165fcc6b3483b09bfc740 Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 10 May 2022 03:48:48 +0100 Subject: [PATCH 073/203] Cache xap specs --- lib/python/qmk/cli/xap/xap.py | 2 +- lib/python/qmk/xap/common.py | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index a90625e748ef..e0af627ef328 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -10,7 +10,7 @@ from qmk.xap.common import get_xap_keycodes -KEYCODE_MAP = get_xap_keycodes('0.2.0') +KEYCODE_MAP = get_xap_keycodes('latest') def _is_xap_usage(x): diff --git a/lib/python/qmk/xap/common.py b/lib/python/qmk/xap/common.py index 1a39682e099b..8d68de3fab21 100755 --- a/lib/python/qmk/xap/common.py +++ b/lib/python/qmk/xap/common.py @@ -8,6 +8,7 @@ from qmk.constants import QMK_FIRMWARE from qmk.json_schema import json_load +from qmk.decorators import lru_cache def _get_jinja2_env(data_templates_xap_subdir: str): @@ -74,26 +75,28 @@ def update_xap_definitions(original, new): return _merge_ordered_dicts([original, new]) -def latest_xap_defs(): - """Gets the latest version of the XAP definitions. - """ - definitions = [hjson.load(file.open(encoding='utf-8')) for file in get_xap_definition_files()] - return _merge_ordered_dicts(definitions) - - +@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 - index = [idx for idx, s in enumerate(files) if version in str(s)][0] - files = files[:(index + 1)] + 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') + + +@lru_cache(timeout=5) def get_xap_keycodes(xap_version): """Gets keycode data for the required version of the XAP definitions. """ From 3d9c2fd845e33f5a133db295d54f483e77cad40d Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 10 May 2022 18:23:38 +0100 Subject: [PATCH 074/203] Fix duplicate keys --- data/constants/keycodes_0.0.1.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/constants/keycodes_0.0.1.json b/data/constants/keycodes_0.0.1.json index ea0fd4257d6c..a3a1537ab13b 100644 --- a/data/constants/keycodes_0.0.1.json +++ b/data/constants/keycodes_0.0.1.json @@ -689,7 +689,7 @@ }, "0x006d": { "group": "basic", - "key": "KC_F17", + "key": "KC_F18", "label": "F18" }, "0x006e": { @@ -1380,7 +1380,7 @@ }, "0x00f0": { "group": "mouse", - "key": "KC_MS_LEFT", + "key": "KC_MS_RIGHT", "label": "Move cursor right", "aliases": [ "KC_MS_R" From 68208278e087dd7c8ce0c4709d79cb6ab5e8941c Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 11 May 2022 01:52:48 +0100 Subject: [PATCH 075/203] Render layers with 'qmk info' logic --- data/constants/keycodes_0.0.1.json | 6 +++--- lib/python/qmk/cli/xap/xap.py | 27 +++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/data/constants/keycodes_0.0.1.json b/data/constants/keycodes_0.0.1.json index a3a1537ab13b..7644ba223b6b 100644 --- a/data/constants/keycodes_0.0.1.json +++ b/data/constants/keycodes_0.0.1.json @@ -210,7 +210,7 @@ "0x0028": { "group": "basic", "key": "KC_ENTER", - "label": "0", + "label": "Enter", "aliases": [ "KC_ENT" ] @@ -218,7 +218,7 @@ "0x0029": { "group": "basic", "key": "KC_ESCAPE", - "label": "0", + "label": "Esc", "aliases": [ "KC_ESC" ] @@ -226,7 +226,7 @@ "0x002a": { "group": "basic", "key": "KC_BACKSPACE", - "label": "0", + "label": "Backspace", "aliases": [ "KC_BSPC" ] diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index e0af627ef328..8de1b5680827 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -8,6 +8,7 @@ from milc import cli +from qmk.keyboard import render_layout from qmk.xap.common import get_xap_keycodes KEYCODE_MAP = get_xap_keycodes('latest') @@ -86,7 +87,7 @@ def _xap_transaction(device, sub, route, *args): device.write(buffer) # get resp - array_alpha = device.read(64, 100) + array_alpha = device.read(64, 250) # validate tok sent == resp if str(token) != str(array_alpha[:2]): @@ -250,7 +251,6 @@ def do_keymap(self, arg): rows = info['matrix_size']['rows'] cols = info['matrix_size']['cols'] - # TODO: render like qmk info? for r in range(rows): for c in range(cols): q = data + r.to_bytes(1, byteorder='little') + c.to_bytes(1, byteorder='little') @@ -259,6 +259,29 @@ def do_keymap(self, arg): 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 = _query_device_info(self.device) + + # 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 = _xap_transaction(self.device, 0x04, 0x02, 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 """ From cb7d103ba8ac5ecfefb907ee71d13cd305bd1e07 Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 11 May 2022 01:53:03 +0100 Subject: [PATCH 076/203] wider keys? --- lib/python/qmk/keyboard.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/python/qmk/keyboard.py b/lib/python/qmk/keyboard.py index e69f63aebeb5..9a3cd4e29f5e 100644 --- a/lib/python/qmk/keyboard.py +++ b/lib/python/qmk/keyboard.py @@ -4,6 +4,7 @@ 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.json_schema import json_load from qmk.makefile import parse_rules_mk_file +KEY_WIDTH = 4 if shutil.get_terminal_size().columns < 200 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 From a8cbda7b8ab013c540ca4594c51bcfaed0911897 Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 11 May 2022 03:25:53 +0100 Subject: [PATCH 077/203] lower threshold --- lib/python/qmk/keyboard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/python/qmk/keyboard.py b/lib/python/qmk/keyboard.py index 9a3cd4e29f5e..cb201c80e397 100644 --- a/lib/python/qmk/keyboard.py +++ b/lib/python/qmk/keyboard.py @@ -12,7 +12,7 @@ from qmk.json_schema import json_load from qmk.makefile import parse_rules_mk_file -KEY_WIDTH = 4 if shutil.get_terminal_size().columns < 200 else 6 +KEY_WIDTH = 4 if shutil.get_terminal_size().columns < 160 else 6 BOX_DRAWING_CHARACTERS = { "unicode": { "tl": "┌", From a9468f385ed9e415cea91c23ca0a0de0e8b35c12 Mon Sep 17 00:00:00 2001 From: zvecr Date: Sun, 22 May 2022 16:01:37 +0100 Subject: [PATCH 078/203] stub out route and broadcast_message docs --- .../xap/docs/broadcast_messages.md.j2 | 4 + data/templates/xap/docs/routes.md.j2 | 12 +++ data/xap/xap_0.0.1.hjson | 12 +++ data/xap/xap_0.1.0.hjson | 2 + docs/_summary.md | 1 + docs/xap.md | 89 ++++++++++++++++++ docs/xap_0.0.1.md | 17 ++++ docs/xap_0.1.0.md | 61 +++++++++++++ docs/xap_0.2.0.md | 90 +++++++++++++++++++ docs/xap_protocol.md | 3 +- lib/python/qmk/cli/xap/generate_docs.py | 3 +- 11 files changed, 290 insertions(+), 4 deletions(-) create mode 100644 data/templates/xap/docs/broadcast_messages.md.j2 create mode 100644 data/templates/xap/docs/routes.md.j2 create mode 100644 docs/xap.md diff --git a/data/templates/xap/docs/broadcast_messages.md.j2 b/data/templates/xap/docs/broadcast_messages.md.j2 new file mode 100644 index 000000000000..9e4ca953f1d6 --- /dev/null +++ b/data/templates/xap/docs/broadcast_messages.md.j2 @@ -0,0 +1,4 @@ +{%- for id, message in xap.broadcast_messages.messages | dictsort %} +### {{ message.name }} - `{{ id }}` +{{ message.description }} +{%- endfor %} diff --git a/data/templates/xap/docs/routes.md.j2 b/data/templates/xap/docs/routes.md.j2 new file mode 100644 index 000000000000..339d58e6168f --- /dev/null +++ b/data/templates/xap/docs/routes.md.j2 @@ -0,0 +1,12 @@ +{%- for id, route in xap.routes | dictsort %} +### {{ route.name }} - `{{ id }}` +{{ route.description }} + +{% if route.routes %} +| Name | Route | Definition | +| -- | -- | -- | +{%- for subid, subroute in route.routes | dictsort %} +| {{ subroute.name }} | `{{ id }} {{ subid }}` | {{ subroute.description.split('\n')[0] }} | +{%- endfor %} +{% endif %} +{%- endfor %} diff --git a/data/xap/xap_0.0.1.hjson b/data/xap/xap_0.0.1.hjson index 48325247bf69..d22c54286460 100755 --- a/data/xap/xap_0.0.1.hjson +++ b/data/xap/xap_0.0.1.hjson @@ -17,6 +17,8 @@ response_flags !response_flags.md.j2 example_conversation + routes + !routes.md.j2 ] page_header: @@ -64,17 +66,27 @@ ### 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: { diff --git a/data/xap/xap_0.1.0.hjson b/data/xap/xap_0.1.0.hjson index 078997a0c541..fb5731d4bf3a 100755 --- a/data/xap/xap_0.1.0.hjson +++ b/data/xap/xap_0.1.0.hjson @@ -8,6 +8,7 @@ documentation: { order: [ broadcast_messages + !broadcast_messages.md.j2 ] reserved_tokens: @@ -118,6 +119,7 @@ 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 | diff --git a/docs/_summary.md b/docs/_summary.md index fed75196b4f1..aa96e4c2e643 100644 --- a/docs/_summary.md +++ b/docs/_summary.md @@ -92,6 +92,7 @@ * [Unicode](feature_unicode.md) * [Userspace](feature_userspace.md) * [WPM Calculation](feature_wpm.md) + * [XAP](xap.md) * Hardware Features * Displays diff --git a/docs/xap.md b/docs/xap.md new file mode 100644 index 000000000000..340c42bca836 --- /dev/null +++ b/docs/xap.md @@ -0,0 +1,89 @@ +# XAP + +XAP (“extensible application protocol”) API intends to provide access to various QMK subsystems. + +## Overview + +TODO + +## 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 ): +======================================== +EOF about exit help keycode keymap layer listen unlock + +Ψ> +``` diff --git a/docs/xap_0.0.1.md b/docs/xap_0.0.1.md index 313ab9e488a1..e8d165ef10e8 100644 --- a/docs/xap_0.0.1.md +++ b/docs/xap_0.0.1.md @@ -52,14 +52,31 @@ Response messages will always be prefixed by the originating request _token_, di ### 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 | Definition | +| -- | -- | -- | +| Version Query | `0x00 0x00` | XAP protocol version query. | + + diff --git a/docs/xap_0.1.0.md b/docs/xap_0.1.0.md index 73a76680c573..6e79158e3dca 100644 --- a/docs/xap_0.1.0.md +++ b/docs/xap_0.1.0.md @@ -65,18 +65,79 @@ Response messages will always be prefixed by the originating request _token_, di ### 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 | Definition | +| -- | -- | -- | +| Version Query | `0x00 0x00` | XAP protocol version query. | +| Capabilities Query | `0x00 0x01` | XAP subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | +| Enabled subsystem query | `0x00 0x02` | 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` | Query secure route status | +| 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 | Definition | +| -- | -- | -- | +| Version Query | `0x01 0x00` | QMK protocol version query. | +| Capabilities Query | `0x01 0x01` | QMK subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | +| Board identifiers | `0x01 0x02` | Retrieves the set of identifying information for the board. | +| Board Manufacturer | `0x01 0x03` | Retrieves the name of the manufacturer | +| Product Name | `0x01 0x04` | Retrieves the product name | +| info.json length | `0x01 0x05` | Retrieves the length of info.json | +| info.json | `0x01 0x06` | Retrieves a chunk of info.json | +| Jump to bootloader | `0x01 0x07` | Jump to bootloader | +| info.json | `0x01 0x08` | Retrieves a unique identifier for the board. | + +### Keyboard - `0x02` +This subsystem is always present, and reserved for user-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`( ) | `0x51`(Q) | `0x4D`(M) | `0x4B`(K) | `0x21`(!) | +### Secure Status - `0x01` +Secure status has changed. + diff --git a/docs/xap_0.2.0.md b/docs/xap_0.2.0.md index 73a76680c573..47115c5e365f 100644 --- a/docs/xap_0.2.0.md +++ b/docs/xap_0.2.0.md @@ -65,18 +65,108 @@ Response messages will always be prefixed by the originating request _token_, di ### 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 | Definition | +| -- | -- | -- | +| Version Query | `0x00 0x00` | XAP protocol version query. | +| Capabilities Query | `0x00 0x01` | XAP subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | +| Enabled subsystem query | `0x00 0x02` | 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` | Query secure route status | +| 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 | Definition | +| -- | -- | -- | +| Version Query | `0x01 0x00` | QMK protocol version query. | +| Capabilities Query | `0x01 0x01` | QMK subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | +| Board identifiers | `0x01 0x02` | Retrieves the set of identifying information for the board. | +| Board Manufacturer | `0x01 0x03` | Retrieves the name of the manufacturer | +| Product Name | `0x01 0x04` | Retrieves the product name | +| info.json length | `0x01 0x05` | Retrieves the length of info.json | +| info.json | `0x01 0x06` | Retrieves a chunk of info.json | +| Jump to bootloader | `0x01 0x07` | Jump to bootloader | +| info.json | `0x01 0x08` | Retrieves a unique identifier for the board. | + +### Keyboard - `0x02` +This subsystem is always present, and reserved for user-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. + + +### Dynamic Keymap - `0x04` +This subsystem allows for live modifications of the keymap, allowing keys to be reassigned without rebuilding the firmware. + + +| Name | Route | Definition | +| -- | -- | -- | +| Capabilities Query | `0x04 0x00` | Dynamic Keymap subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | +| Get Layer Count | `0x04 0x01` | TODO | +| Get Keycode | `0x04 0x02` | TODO | +| Set Keycode | `0x04 0x03` | TODO | + +### Dynamic Encoders - `0x05` +This subsystem allows for live modifications of the keymap, allowing encoder functionality to be reassigned without rebuilding the firmware. + + +| Name | Route | Definition | +| -- | -- | -- | +| Capabilities Query | `0x05 0x00` | Dynamic Encoders subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | +| Get Keycode | `0x05 0x02` | TODO | +| Set Keycode | `0x05 0x03` | TODO | + +### Lighting - `0x06` +This subsystem allows for control over the lighting subsystem. + + +| Name | Route | Definition | +| -- | -- | -- | +| Capabilities Query | `0x06 0x00` | 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`( ) | `0x51`(Q) | `0x4D`(M) | `0x4B`(K) | `0x21`(!) | +### Secure Status - `0x01` +Secure status has changed. + diff --git a/docs/xap_protocol.md b/docs/xap_protocol.md index 688d7dab3ea4..08390580bbe0 100644 --- a/docs/xap_protocol.md +++ b/docs/xap_protocol.md @@ -1,5 +1,4 @@ -# XAP Protocol Reference - + * [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) diff --git a/lib/python/qmk/cli/xap/generate_docs.py b/lib/python/qmk/cli/xap/generate_docs.py index e399b6ddb122..ae1c9907b4a0 100755 --- a/lib/python/qmk/cli/xap/generate_docs.py +++ b/lib/python/qmk/cli/xap/generate_docs.py @@ -30,8 +30,7 @@ def xap_generate_docs(cli): output_doc = QMK_FIRMWARE / "docs" / "xap_protocol.md" with open(output_doc, "w", encoding='utf-8') as out_file: out_file.write('''\ -# XAP Protocol Reference - + ''') for file in reversed(sorted(docs_list)): From d75330f9c198e8295ac8a18640484865775afb45 Mon Sep 17 00:00:00 2001 From: zvecr Date: Sun, 22 May 2022 20:55:17 +0100 Subject: [PATCH 079/203] flesh out secure docs --- data/templates/xap/docs/routes.md.j2 | 2 +- data/xap/xap_0.1.0.hjson | 11 +++++++++-- docs/xap_0.1.0.md | 11 +++++++++-- docs/xap_0.2.0.md | 11 +++++++++-- 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/data/templates/xap/docs/routes.md.j2 b/data/templates/xap/docs/routes.md.j2 index 339d58e6168f..6b27f9677559 100644 --- a/data/templates/xap/docs/routes.md.j2 +++ b/data/templates/xap/docs/routes.md.j2 @@ -6,7 +6,7 @@ | Name | Route | Definition | | -- | -- | -- | {%- for subid, subroute in route.routes | dictsort %} -| {{ subroute.name }} | `{{ id }} {{ subid }}` | {{ subroute.description.split('\n')[0] }} | +| {{ subroute.name }} | `{{ id }} {{ subid }}` | {% if 'secure' == subroute.permissions %}__Secure unlock required__ {% endif %}{{ subroute.description.split('\n')[0] }} | {%- endfor %} {% endif %} {%- endfor %} diff --git a/data/xap/xap_0.1.0.hjson b/data/xap/xap_0.1.0.hjson index fb5731d4bf3a..aea65843465f 100755 --- a/data/xap/xap_0.1.0.hjson +++ b/data/xap/xap_0.1.0.hjson @@ -119,7 +119,7 @@ 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 | @@ -131,7 +131,14 @@ define: SECURE_STATUS description: ''' - Secure status has changed. + 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 } diff --git a/docs/xap_0.1.0.md b/docs/xap_0.1.0.md index 6e79158e3dca..4ce1deb43bac 100644 --- a/docs/xap_0.1.0.md +++ b/docs/xap_0.1.0.md @@ -110,7 +110,7 @@ This subsystem is always present, and provides the ability to address QMK-specif | Product Name | `0x01 0x04` | Retrieves the product name | | info.json length | `0x01 0x05` | Retrieves the length of info.json | | info.json | `0x01 0x06` | Retrieves a chunk of info.json | -| Jump to bootloader | `0x01 0x07` | Jump to bootloader | +| Jump to bootloader | `0x01 0x07` | __Secure unlock required__ Jump to bootloader | | info.json | `0x01 0x08` | Retrieves a unique identifier for the board. | ### Keyboard - `0x02` @@ -139,5 +139,12 @@ Log message payloads include a `u8` signifying the length of the text, followed | **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`( ) | `0x51`(Q) | `0x4D`(M) | `0x4B`(K) | `0x21`(!) | ### Secure Status - `0x01` -Secure status has changed. +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` | diff --git a/docs/xap_0.2.0.md b/docs/xap_0.2.0.md index 47115c5e365f..e48d1a323152 100644 --- a/docs/xap_0.2.0.md +++ b/docs/xap_0.2.0.md @@ -110,7 +110,7 @@ This subsystem is always present, and provides the ability to address QMK-specif | Product Name | `0x01 0x04` | Retrieves the product name | | info.json length | `0x01 0x05` | Retrieves the length of info.json | | info.json | `0x01 0x06` | Retrieves a chunk of info.json | -| Jump to bootloader | `0x01 0x07` | Jump to bootloader | +| Jump to bootloader | `0x01 0x07` | __Secure unlock required__ Jump to bootloader | | info.json | `0x01 0x08` | Retrieves a unique identifier for the board. | ### Keyboard - `0x02` @@ -168,5 +168,12 @@ Log message payloads include a `u8` signifying the length of the text, followed | **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`( ) | `0x51`(Q) | `0x4D`(M) | `0x4B`(K) | `0x21`(!) | ### Secure Status - `0x01` -Secure status has changed. +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` | From d95d22201a66c239a038cc88ccf1c798ca586c91 Mon Sep 17 00:00:00 2001 From: zvecr Date: Sun, 22 May 2022 21:12:32 +0100 Subject: [PATCH 080/203] multiline description? --- data/templates/xap/docs/routes.md.j2 | 6 +-- data/xap/xap_0.2.0.hjson | 2 + docs/xap_0.0.1.md | 6 +-- docs/xap_0.1.0.md | 38 ++++++++-------- docs/xap_0.2.0.md | 66 ++++++++++++++-------------- 5 files changed, 60 insertions(+), 58 deletions(-) diff --git a/data/templates/xap/docs/routes.md.j2 b/data/templates/xap/docs/routes.md.j2 index 6b27f9677559..c740ae2f5e1e 100644 --- a/data/templates/xap/docs/routes.md.j2 +++ b/data/templates/xap/docs/routes.md.j2 @@ -3,10 +3,10 @@ {{ route.description }} {% if route.routes %} -| Name | Route | Definition | -| -- | -- | -- | +| Name | Route | Tags | Definition | +| -- | -- | -- | -- | {%- for subid, subroute in route.routes | dictsort %} -| {{ subroute.name }} | `{{ id }} {{ subid }}` | {% if 'secure' == subroute.permissions %}__Secure unlock required__ {% endif %}{{ subroute.description.split('\n')[0] }} | +| {{ subroute.name }} | `{{ id }} {{ subid }}` | {% if 'secure' == subroute.permissions %}__Secure__{% endif %} | {{ subroute.description.replace('\n', '
') }} | {%- endfor %} {% endif %} {%- endfor %} diff --git a/data/xap/xap_0.2.0.hjson b/data/xap/xap_0.2.0.hjson index 6fd985314827..822e5190a08d 100755 --- a/data/xap/xap_0.2.0.hjson +++ b/data/xap/xap_0.2.0.hjson @@ -61,6 +61,7 @@ name: Set Keycode define: SET_KEYMAP_KEYCODE description: TODO + permissions: secure request_type: struct request_struct_length: 5 request_struct_members: [ @@ -136,6 +137,7 @@ type: command name: Set Keycode define: SET_ENCODER_KEYCODE + permissions: secure description: TODO request_type: struct request_struct_length: 5 diff --git a/docs/xap_0.0.1.md b/docs/xap_0.0.1.md index e8d165ef10e8..20e6115a7cfa 100644 --- a/docs/xap_0.0.1.md +++ b/docs/xap_0.0.1.md @@ -75,8 +75,8 @@ This is the primary method for determining if a subsystem has been enabled in th This subsystem is always present, and provides the ability to query information about the XAP protocol of the connected device. -| Name | Route | Definition | -| -- | -- | -- | -| Version Query | `0x00 0x00` | XAP protocol version query. | +| Name | Route | Tags | Definition | +| -- | -- | -- | -- | +| Version Query | `0x00 0x00` | | 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}.
* Response:
* `u32` value. | diff --git a/docs/xap_0.1.0.md b/docs/xap_0.1.0.md index 4ce1deb43bac..de21009a9cc4 100644 --- a/docs/xap_0.1.0.md +++ b/docs/xap_0.1.0.md @@ -88,30 +88,30 @@ This is the primary method for determining if a subsystem has been enabled in th This subsystem is always present, and provides the ability to query information about the XAP protocol of the connected device. -| Name | Route | Definition | -| -- | -- | -- | -| Version Query | `0x00 0x00` | XAP protocol version query. | -| Capabilities Query | `0x00 0x01` | XAP subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | -| Enabled subsystem query | `0x00 0x02` | 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` | Query secure route status | -| Secure Unlock | `0x00 0x04` | Initiate secure route unlock sequence | -| Secure Lock | `0x00 0x05` | Disable secure routes | +| Name | Route | Tags | Definition | +| -- | -- | -- | -- | +| Version Query | `0x00 0x00` | | 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}.
* Response:
* `u32` value. | +| Capabilities Query | `0x00 0x01` | | XAP subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | +| Enabled subsystem query | `0x00 0x02` | | 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` | | 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 | +| 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 | Definition | -| -- | -- | -- | -| Version Query | `0x01 0x00` | QMK protocol version query. | -| Capabilities Query | `0x01 0x01` | QMK subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | -| Board identifiers | `0x01 0x02` | Retrieves the set of identifying information for the board. | -| Board Manufacturer | `0x01 0x03` | Retrieves the name of the manufacturer | -| Product Name | `0x01 0x04` | Retrieves the product name | -| info.json length | `0x01 0x05` | Retrieves the length of info.json | -| info.json | `0x01 0x06` | Retrieves a chunk of info.json | -| Jump to bootloader | `0x01 0x07` | __Secure unlock required__ Jump to bootloader | -| info.json | `0x01 0x08` | Retrieves a unique identifier for the board. | +| Name | Route | Tags | Definition | +| -- | -- | -- | -- | +| Version Query | `0x01 0x00` | | 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}.
* Response:
* `u32` value. | +| Capabilities Query | `0x01 0x01` | | QMK subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | +| Board identifiers | `0x01 0x02` | | Retrieves the set of identifying information for the board. | +| Board Manufacturer | `0x01 0x03` | | Retrieves the name of the manufacturer | +| Product Name | `0x01 0x04` | | Retrieves the product name | +| info.json length | `0x01 0x05` | | Retrieves the length of info.json | +| info.json | `0x01 0x06` | | Retrieves a chunk of info.json | +| Jump to bootloader | `0x01 0x07` | __Secure__ | 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 | +| info.json | `0x01 0x08` | | Retrieves a unique identifier for the board. | ### Keyboard - `0x02` This subsystem is always present, and reserved for user-specific functionality. No routes are defined by XAP. diff --git a/docs/xap_0.2.0.md b/docs/xap_0.2.0.md index e48d1a323152..fc8afa4ebf3a 100644 --- a/docs/xap_0.2.0.md +++ b/docs/xap_0.2.0.md @@ -88,30 +88,30 @@ This is the primary method for determining if a subsystem has been enabled in th This subsystem is always present, and provides the ability to query information about the XAP protocol of the connected device. -| Name | Route | Definition | -| -- | -- | -- | -| Version Query | `0x00 0x00` | XAP protocol version query. | -| Capabilities Query | `0x00 0x01` | XAP subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | -| Enabled subsystem query | `0x00 0x02` | 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` | Query secure route status | -| Secure Unlock | `0x00 0x04` | Initiate secure route unlock sequence | -| Secure Lock | `0x00 0x05` | Disable secure routes | +| Name | Route | Tags | Definition | +| -- | -- | -- | -- | +| Version Query | `0x00 0x00` | | 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}.
* Response:
* `u32` value. | +| Capabilities Query | `0x00 0x01` | | XAP subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | +| Enabled subsystem query | `0x00 0x02` | | 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` | | 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 | +| 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 | Definition | -| -- | -- | -- | -| Version Query | `0x01 0x00` | QMK protocol version query. | -| Capabilities Query | `0x01 0x01` | QMK subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | -| Board identifiers | `0x01 0x02` | Retrieves the set of identifying information for the board. | -| Board Manufacturer | `0x01 0x03` | Retrieves the name of the manufacturer | -| Product Name | `0x01 0x04` | Retrieves the product name | -| info.json length | `0x01 0x05` | Retrieves the length of info.json | -| info.json | `0x01 0x06` | Retrieves a chunk of info.json | -| Jump to bootloader | `0x01 0x07` | __Secure unlock required__ Jump to bootloader | -| info.json | `0x01 0x08` | Retrieves a unique identifier for the board. | +| Name | Route | Tags | Definition | +| -- | -- | -- | -- | +| Version Query | `0x01 0x00` | | 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}.
* Response:
* `u32` value. | +| Capabilities Query | `0x01 0x01` | | QMK subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | +| Board identifiers | `0x01 0x02` | | Retrieves the set of identifying information for the board. | +| Board Manufacturer | `0x01 0x03` | | Retrieves the name of the manufacturer | +| Product Name | `0x01 0x04` | | Retrieves the product name | +| info.json length | `0x01 0x05` | | Retrieves the length of info.json | +| info.json | `0x01 0x06` | | Retrieves a chunk of info.json | +| Jump to bootloader | `0x01 0x07` | __Secure__ | 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 | +| info.json | `0x01 0x08` | | Retrieves a unique identifier for the board. | ### Keyboard - `0x02` This subsystem is always present, and reserved for user-specific functionality. No routes are defined by XAP. @@ -125,30 +125,30 @@ This subsystem is always present, and reserved for user-specific functionality. This subsystem allows for live modifications of the keymap, allowing keys to be reassigned without rebuilding the firmware. -| Name | Route | Definition | -| -- | -- | -- | -| Capabilities Query | `0x04 0x00` | Dynamic Keymap subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | -| Get Layer Count | `0x04 0x01` | TODO | -| Get Keycode | `0x04 0x02` | TODO | -| Set Keycode | `0x04 0x03` | TODO | +| Name | Route | Tags | Definition | +| -- | -- | -- | -- | +| Capabilities Query | `0x04 0x00` | | Dynamic Keymap subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | +| Get Layer Count | `0x04 0x01` | | TODO | +| Get Keycode | `0x04 0x02` | | TODO | +| Set Keycode | `0x04 0x03` | __Secure__ | TODO | ### Dynamic Encoders - `0x05` This subsystem allows for live modifications of the keymap, allowing encoder functionality to be reassigned without rebuilding the firmware. -| Name | Route | Definition | -| -- | -- | -- | -| Capabilities Query | `0x05 0x00` | Dynamic Encoders subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | -| Get Keycode | `0x05 0x02` | TODO | -| Set Keycode | `0x05 0x03` | TODO | +| Name | Route | Tags | Definition | +| -- | -- | -- | -- | +| Capabilities Query | `0x05 0x00` | | Dynamic Encoders subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | +| Get Keycode | `0x05 0x02` | | TODO | +| Set Keycode | `0x05 0x03` | __Secure__ | TODO | ### Lighting - `0x06` This subsystem allows for control over the lighting subsystem. -| Name | Route | Definition | -| -- | -- | -- | -| Capabilities Query | `0x06 0x00` | Lighting subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | +| Name | Route | Tags | Definition | +| -- | -- | -- | -- | +| Capabilities Query | `0x06 0x00` | | Lighting subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | ## Broadcast messages From 17dbcedc8b846aec007f91dd0274a42b0d4444a2 Mon Sep 17 00:00:00 2001 From: zvecr Date: Sun, 22 May 2022 22:47:50 +0100 Subject: [PATCH 081/203] bind xap_broadcast_secure_status to secure_hook_quantum --- lib/python/qmk/xap/gen_firmware/header_generator.py | 7 +++++++ quantum/keyboard.c | 4 ---- quantum/quantum.c | 4 ++++ quantum/xap/xap.c | 12 ------------ quantum/xap/xap.h | 2 -- 5 files changed, 11 insertions(+), 18 deletions(-) diff --git a/lib/python/qmk/xap/gen_firmware/header_generator.py b/lib/python/qmk/xap/gen_firmware/header_generator.py index 7ce1d9d84ed1..0a9ba7beadd6 100755 --- a/lib/python/qmk/xap/gen_firmware/header_generator.py +++ b/lib/python/qmk/xap/gen_firmware/header_generator.py @@ -194,7 +194,14 @@ def _append_internal_types(lines, container): 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') diff --git a/quantum/keyboard.c b/quantum/keyboard.c index 15b4b7c26579..a65f9d6d1865 100644 --- a/quantum/keyboard.c +++ b/quantum/keyboard.c @@ -559,10 +559,6 @@ void quantum_task(void) { #ifdef SECURE_ENABLE secure_task(); #endif - -#ifdef XAP_ENABLE - xap_event_task(); -#endif } /** \brief Keyboard task: Do keyboard routine jobs diff --git a/quantum/quantum.c b/quantum/quantum.c index ac3e2d90b4d9..004010c565da 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c @@ -582,5 +582,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 diff --git a/quantum/xap/xap.c b/quantum/xap/xap.c index d4b6eb80fa7e..52030639edd9 100644 --- a/quantum/xap/xap.c +++ b/quantum/xap/xap.c @@ -158,15 +158,3 @@ void xap_execute_route(xap_token_t token, const xap_route_t *routes, size_t max_ 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); } - -void xap_event_task(void) { -#ifdef SECURE_ENABLE - static secure_status_t last_status = -1; - - secure_status_t status = secure_get_status(); - if (last_status != status) { - last_status = status; - xap_broadcast_secure_status(status); - } -#endif -} diff --git a/quantum/xap/xap.h b/quantum/xap/xap.h index b1ecd8cb9ca2..76e509549296 100644 --- a/quantum/xap/xap.h +++ b/quantum/xap/xap.h @@ -36,5 +36,3 @@ 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); - -void xap_event_task(void); From b1632946c91f6f1c8c83a23fdd4da88e68c19f0b Mon Sep 17 00:00:00 2001 From: zvecr Date: Mon, 23 May 2022 00:12:36 +0100 Subject: [PATCH 082/203] stub out keyboard/keymap level xap specs --- builddefs/xap.mk | 4 +- lib/python/qmk/cli/xap/generate_qmk.py | 26 ++++++++++-- lib/python/qmk/xap/common.py | 42 ++++++++++++++++++- .../qmk/xap/gen_firmware/header_generator.py | 6 +-- .../qmk/xap/gen_firmware/inline_generator.py | 6 +-- 5 files changed, 71 insertions(+), 13 deletions(-) diff --git a/builddefs/xap.mk b/builddefs/xap.mk index e599c8122113..4f23c28467ad 100644 --- a/builddefs/xap.mk +++ b/builddefs/xap.mk @@ -11,12 +11,12 @@ XAP_FILES := $(shell ls -1 data/xap/* | sort | xargs echo) $(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") + $(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)) + $(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/info_json_gz.h $(KEYMAP_OUTPUT)/src/xap_generated.inl $(KEYMAP_OUTPUT)/src/xap_generated.h diff --git a/lib/python/qmk/cli/xap/generate_qmk.py b/lib/python/qmk/cli/xap/generate_qmk.py index 26476dba46f3..d4ebb7e9dbde 100755 --- a/lib/python/qmk/cli/xap/generate_qmk.py +++ b/lib/python/qmk/cli/xap/generate_qmk.py @@ -10,25 +10,43 @@ @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. """ - generate_inline(cli.args.output) + # 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 + # Determine our keyboard/keymap if not cli.args.keyboard: cli.log.error('Missing parameter: --keyboard') - cli.subcommands['xap-generate-qmk-h'].print_help() + cli.subcommands['xap-generate-info-h'].print_help() return False - generate_header(cli.args.output, cli.args.keyboard) + 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') diff --git a/lib/python/qmk/xap/common.py b/lib/python/qmk/xap/common.py index 8d68de3fab21..c28f9bea2bec 100755 --- a/lib/python/qmk/xap/common.py +++ b/lib/python/qmk/xap/common.py @@ -9,6 +9,8 @@ from qmk.constants import QMK_FIRMWARE from qmk.json_schema import json_load from qmk.decorators import lru_cache +from qmk.keymap import locate_keymap +from qmk.path import keyboard def _get_jinja2_env(data_templates_xap_subdir: str): @@ -33,7 +35,7 @@ def _merge_ordered_dicts(dicts): result = OrderedDict() def add_entry(target, k, v): - if k in target and isinstance(v, OrderedDict): + if k in target and isinstance(v, (OrderedDict, dict)): if "!reset!" in v: target[k] = v else: @@ -96,6 +98,44 @@ def latest_xap_defs(): return get_xap_defs('latest') +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.json' + if spec.exists(): + return spec + + keyboard_parent = keyboard_parent.parent + + # Just return something we know doesn't exist + return keyboard(kb) / 'xap.json' + + +def _find_km_spec(kb, km): + return locate_keymap(kb, km).parent / 'xap.json' + + +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'))}}) + + return _merge_ordered_dicts(definitions) + + @lru_cache(timeout=5) def get_xap_keycodes(xap_version): """Gets keycode data for the required version of the XAP definitions. diff --git a/lib/python/qmk/xap/gen_firmware/header_generator.py b/lib/python/qmk/xap/gen_firmware/header_generator.py index 0a9ba7beadd6..bd54c93720d2 100755 --- a/lib/python/qmk/xap/gen_firmware/header_generator.py +++ b/lib/python/qmk/xap/gen_firmware/header_generator.py @@ -7,7 +7,7 @@ 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 latest_xap_defs, route_conditions +from qmk.xap.common import merge_xap_defs, route_conditions def _get_c_type(xap_type): @@ -233,10 +233,10 @@ def _append_internal_types(lines, container): lines.append(f'typedef {data_type} xap_{key}_t;') -def generate_header(output_file, keyboard): +def generate_header(output_file, keyboard, keymap): """Generates the XAP protocol header file, generated during normal build. """ - xap_defs = latest_xap_defs() + xap_defs = merge_xap_defs(keyboard, keymap) # Preamble lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', ''] diff --git a/lib/python/qmk/xap/gen_firmware/inline_generator.py b/lib/python/qmk/xap/gen_firmware/inline_generator.py index 8aeb35604776..44170a18acdc 100755 --- a/lib/python/qmk/xap/gen_firmware/inline_generator.py +++ b/lib/python/qmk/xap/gen_firmware/inline_generator.py @@ -3,7 +3,7 @@ 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 latest_xap_defs, route_conditions +from qmk.xap.common import merge_xap_defs, route_conditions def _get_c_type(xap_type): @@ -268,10 +268,10 @@ def _append_broadcast_messages(lines, container): lines.append(f'void {name}(const void *data, size_t length){{ xap_broadcast({key}, data, length); }}') -def generate_inline(output_file): +def generate_inline(output_file, keyboard, keymap): """Generates the XAP protocol header file, generated during normal build. """ - xap_defs = latest_xap_defs() + xap_defs = merge_xap_defs(keyboard, keymap) # Preamble lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, ''] From e04e31cde7ccb15337ded5bac8c96ffe461b84a8 Mon Sep 17 00:00:00 2001 From: zvecr Date: Mon, 23 May 2022 01:25:32 +0100 Subject: [PATCH 083/203] Extend make rules to include user/keyboard xap.json files --- builddefs/xap.mk | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/builddefs/xap.mk b/builddefs/xap.mk index 4f23c28467ad..8fbda7c44881 100644 --- a/builddefs/xap.mk +++ b/builddefs/xap.mk @@ -8,6 +8,24 @@ $(KEYMAP_OUTPUT)/src/info_json_gz.h: $(INFO_JSON_FILES) @$(BUILD_CMD) XAP_FILES := $(shell ls -1 data/xap/* | sort | xargs echo) +ifneq ("$(wildcard $(KEYBOARD_PATH_1)/xap.json)","") + XAP_FILES += $(KEYBOARD_PATH_1)/xap.json +endif +ifneq ("$(wildcard $(KEYBOARD_PATH_2)/xap.json)","") + XAP_FILES += $(KEYBOARD_PATH_2)/xap.json +endif +ifneq ("$(wildcard $(KEYBOARD_PATH_3)/xap.json)","") + XAP_FILES += $(KEYBOARD_PATH_3)/xap.json +endif +ifneq ("$(wildcard $(KEYBOARD_PATH_4)/xap.json)","") + XAP_FILES += $(KEYBOARD_PATH_4)/xap.json +endif +ifneq ("$(wildcard $(KEYBOARD_PATH_5)/xap.json)","") + XAP_FILES += $(KEYBOARD_PATH_5)/xap.json +endif +ifneq ("$(wildcard $(KEYMAP_PATH)/xap.json)","") + XAP_FILES += $(KEYMAP_PATH)/xap.json +endif $(KEYMAP_OUTPUT)/src/xap_generated.inl: $(XAP_FILES) @$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD) From f2d56f5ca1bb588cf594ff2618d1aad3336c5f1f Mon Sep 17 00:00:00 2001 From: zvecr Date: Mon, 23 May 2022 02:14:42 +0100 Subject: [PATCH 084/203] Align filenames --- builddefs/xap.mk | 24 +++++------ keyboards/zvecr/zv48/keymaps/xap/xap.hjson | 16 ++++++++ keyboards/zvecr/zv48/xap.hjson | 16 ++++++++ lib/python/qmk/xap/common.py | 46 +++++++++++----------- 4 files changed, 68 insertions(+), 34 deletions(-) create mode 100644 keyboards/zvecr/zv48/keymaps/xap/xap.hjson create mode 100644 keyboards/zvecr/zv48/xap.hjson diff --git a/builddefs/xap.mk b/builddefs/xap.mk index 8fbda7c44881..f08d774297fb 100644 --- a/builddefs/xap.mk +++ b/builddefs/xap.mk @@ -8,23 +8,23 @@ $(KEYMAP_OUTPUT)/src/info_json_gz.h: $(INFO_JSON_FILES) @$(BUILD_CMD) XAP_FILES := $(shell ls -1 data/xap/* | sort | xargs echo) -ifneq ("$(wildcard $(KEYBOARD_PATH_1)/xap.json)","") - XAP_FILES += $(KEYBOARD_PATH_1)/xap.json +ifneq ("$(wildcard $(KEYBOARD_PATH_1)/xap.hjson)","") + XAP_FILES += $(KEYBOARD_PATH_1)/xap.hjson endif -ifneq ("$(wildcard $(KEYBOARD_PATH_2)/xap.json)","") - XAP_FILES += $(KEYBOARD_PATH_2)/xap.json +ifneq ("$(wildcard $(KEYBOARD_PATH_2)/xap.hjson)","") + XAP_FILES += $(KEYBOARD_PATH_2)/xap.hjson endif -ifneq ("$(wildcard $(KEYBOARD_PATH_3)/xap.json)","") - XAP_FILES += $(KEYBOARD_PATH_3)/xap.json +ifneq ("$(wildcard $(KEYBOARD_PATH_3)/xap.hjson)","") + XAP_FILES += $(KEYBOARD_PATH_3)/xap.hjson endif -ifneq ("$(wildcard $(KEYBOARD_PATH_4)/xap.json)","") - XAP_FILES += $(KEYBOARD_PATH_4)/xap.json +ifneq ("$(wildcard $(KEYBOARD_PATH_4)/xap.hjson)","") + XAP_FILES += $(KEYBOARD_PATH_4)/xap.hjson endif -ifneq ("$(wildcard $(KEYBOARD_PATH_5)/xap.json)","") - XAP_FILES += $(KEYBOARD_PATH_5)/xap.json +ifneq ("$(wildcard $(KEYBOARD_PATH_5)/xap.hjson)","") + XAP_FILES += $(KEYBOARD_PATH_5)/xap.hjson endif -ifneq ("$(wildcard $(KEYMAP_PATH)/xap.json)","") - XAP_FILES += $(KEYMAP_PATH)/xap.json +ifneq ("$(wildcard $(KEYMAP_PATH)/xap.hjson)","") + XAP_FILES += $(KEYMAP_PATH)/xap.hjson endif $(KEYMAP_OUTPUT)/src/xap_generated.inl: $(XAP_FILES) diff --git a/keyboards/zvecr/zv48/keymaps/xap/xap.hjson b/keyboards/zvecr/zv48/keymaps/xap/xap.hjson new file mode 100644 index 000000000000..c406625ccd9c --- /dev/null +++ b/keyboards/zvecr/zv48/keymaps/xap/xap.hjson @@ -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 + } + } +} \ No newline at end of file diff --git a/keyboards/zvecr/zv48/xap.hjson b/keyboards/zvecr/zv48/xap.hjson new file mode 100644 index 000000000000..5da9efc3ecce --- /dev/null +++ b/keyboards/zvecr/zv48/xap.hjson @@ -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 + } + } +} \ No newline at end of file diff --git a/lib/python/qmk/xap/common.py b/lib/python/qmk/xap/common.py index c28f9bea2bec..f3f371cdd8e9 100755 --- a/lib/python/qmk/xap/common.py +++ b/lib/python/qmk/xap/common.py @@ -12,6 +12,8 @@ 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) @@ -24,6 +26,28 @@ def render_xap_output(data_templates_xap_subdir, file_to_render, defs): 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. @@ -98,28 +122,6 @@ def latest_xap_defs(): return get_xap_defs('latest') -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.json' - if spec.exists(): - return spec - - keyboard_parent = keyboard_parent.parent - - # Just return something we know doesn't exist - return keyboard(kb) / 'xap.json' - - -def _find_km_spec(kb, km): - return locate_keymap(kb, km).parent / 'xap.json' - - def merge_xap_defs(kb, km): """Gets the latest version of the XAP definitions and merges in optional keyboard/keymap specs """ From f44a988476bad8c65acc883ce1568c97be8a4a57 Mon Sep 17 00:00:00 2001 From: zvecr Date: Mon, 23 May 2022 20:02:14 +0100 Subject: [PATCH 085/203] Initial validation of xap.hjson --- data/schemas/xap.jsonschema | 254 +++++++++++++++++++++++++++++++++++ lib/python/qmk/xap/common.py | 14 +- 2 files changed, 266 insertions(+), 2 deletions(-) create mode 100644 data/schemas/xap.jsonschema diff --git a/data/schemas/xap.jsonschema b/data/schemas/xap.jsonschema new file mode 100644 index 000000000000..9994778d092e --- /dev/null +++ b/data/schemas/xap.jsonschema @@ -0,0 +1,254 @@ +{ + "$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, + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string" + } + } + } + }, + "route": { + "type": "object", + "propertyNames": { + "$ref": "qmk.definitions.v1#/hex_number_2d" + }, + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "$ref": "#/definitions/router_type" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "define": { + "type": "string" + }, + "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": { + "type": "string" + }, + "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": { + "type": "string" + }, + "bits": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "define": { + "type": "string" + }, + "description": { + "type": "string" + } + } + } + } + } + }, + "broadcast_messages": { + "type": "object", + "additionalProperties": false, + "properties": { + "define_prefix": { + "type": "string" + }, + "messages": { + "type": "object", + "propertyNames": { + "$ref": "qmk.definitions.v1#/hex_number_2d" + }, + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "define": { + "type": "string" + }, + "description": { + "type": "string" + }, + "return_type": { + "$ref": "#/definitions/data_type" + } + } + } + } + } + }, + "routes": { + "$ref": "#/definitions/route" + } + } +} \ No newline at end of file diff --git a/lib/python/qmk/xap/common.py b/lib/python/qmk/xap/common.py index f3f371cdd8e9..64372db804e0 100755 --- a/lib/python/qmk/xap/common.py +++ b/lib/python/qmk/xap/common.py @@ -2,12 +2,13 @@ """ 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 +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 @@ -135,7 +136,16 @@ def merge_xap_defs(kb, km): if km_xap.exists(): definitions.append({'routes': {'0x03': hjson.load(km_xap.open(encoding='utf-8'))}}) - return _merge_ordered_dicts(definitions) + 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) From d83616dce4341285c7304222584cd8fc95201b6d Mon Sep 17 00:00:00 2001 From: zvecr Date: Mon, 23 May 2022 20:26:09 +0100 Subject: [PATCH 086/203] Validate more --- data/schemas/xap.jsonschema | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/data/schemas/xap.jsonschema b/data/schemas/xap.jsonschema index 9994778d092e..537e8aef6e3a 100644 --- a/data/schemas/xap.jsonschema +++ b/data/schemas/xap.jsonschema @@ -3,6 +3,14 @@ "$id": "qmk.xap.v1", "title": "XAP Spec", "definitions": { + "int_key": { + "type": "string", + "pattern": "^[0-8]$" + }, + "constant": { + "type": "string", + "pattern": "^[A-Z_]*$" + }, "data_type": { "oneOf": [ { @@ -68,7 +76,7 @@ "type": "string" }, "define": { - "type": "string" + "$ref": "#/definitions/constant" }, "permissions": { "$ref": "#/definitions/permission" @@ -126,7 +134,7 @@ "$ref": "qmk.definitions.v1#/bcd_version" }, "define": { - "type": "string" + "$ref": "#/definitions/constant" }, "uses": { "type": "object", @@ -189,12 +197,12 @@ "additionalProperties": false, "properties": { "define_prefix": { - "type": "string" + "$ref": "#/definitions/constant" }, "bits": { "type": "object", "propertyNames": { - "type": "string" + "$ref": "#/definitions/int_key" }, "additionalProperties": { "type": "object", @@ -204,7 +212,7 @@ "type": "string" }, "define": { - "type": "string" + "$ref": "#/definitions/constant" }, "description": { "type": "string" @@ -219,7 +227,7 @@ "additionalProperties": false, "properties": { "define_prefix": { - "type": "string" + "$ref": "#/definitions/constant" }, "messages": { "type": "object", @@ -234,7 +242,7 @@ "type": "string" }, "define": { - "type": "string" + "$ref": "#/definitions/constant" }, "description": { "type": "string" From 6d93facc16cd17b2bb7c4adffbd791d27149c582 Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 24 May 2022 01:19:16 +0100 Subject: [PATCH 087/203] bump some definitions to common --- data/schemas/definitions.jsonschema | 10 ++++++++++ data/schemas/xap.jsonschema | 30 ++++++++++++++--------------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/data/schemas/definitions.jsonschema b/data/schemas/definitions.jsonschema index 1bdfbbeb0356..743dec2a0e30 100644 --- a/data/schemas/definitions.jsonschema +++ b/data/schemas/definitions.jsonschema @@ -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, diff --git a/data/schemas/xap.jsonschema b/data/schemas/xap.jsonschema index 537e8aef6e3a..db0e7c4e87fc 100644 --- a/data/schemas/xap.jsonschema +++ b/data/schemas/xap.jsonschema @@ -3,14 +3,6 @@ "$id": "qmk.xap.v1", "title": "XAP Spec", "definitions": { - "int_key": { - "type": "string", - "pattern": "^[0-8]$" - }, - "constant": { - "type": "string", - "pattern": "^[A-Z_]*$" - }, "data_type": { "oneOf": [ { @@ -47,6 +39,10 @@ "items": { "type": "object", "additionalProperties": false, + "required": [ + "name", + "type" + ], "properties": { "name": { "type": "string" @@ -64,6 +60,10 @@ }, "additionalProperties": { "type": "object", + "required": [ + "type", + "define" + ], "additionalProperties": false, "properties": { "type": { @@ -76,7 +76,7 @@ "type": "string" }, "define": { - "$ref": "#/definitions/constant" + "$ref": "qmk.definitions.v1#/define" }, "permissions": { "$ref": "#/definitions/permission" @@ -134,7 +134,7 @@ "$ref": "qmk.definitions.v1#/bcd_version" }, "define": { - "$ref": "#/definitions/constant" + "$ref": "qmk.definitions.v1#/define" }, "uses": { "type": "object", @@ -197,12 +197,12 @@ "additionalProperties": false, "properties": { "define_prefix": { - "$ref": "#/definitions/constant" + "$ref": "qmk.definitions.v1#/define" }, "bits": { "type": "object", "propertyNames": { - "$ref": "#/definitions/int_key" + "$ref": "qmk.definitions.v1#/text_unsigned_int" }, "additionalProperties": { "type": "object", @@ -212,7 +212,7 @@ "type": "string" }, "define": { - "$ref": "#/definitions/constant" + "$ref": "qmk.definitions.v1#/define" }, "description": { "type": "string" @@ -227,7 +227,7 @@ "additionalProperties": false, "properties": { "define_prefix": { - "$ref": "#/definitions/constant" + "$ref": "qmk.definitions.v1#/define" }, "messages": { "type": "object", @@ -242,7 +242,7 @@ "type": "string" }, "define": { - "$ref": "#/definitions/constant" + "$ref": "qmk.definitions.v1#/define" }, "description": { "type": "string" From 32404cbb7764691d3720b47c029264fb9b0fa8f2 Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 24 May 2022 16:15:26 +0100 Subject: [PATCH 088/203] tidy xap.mk --- builddefs/xap.mk | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/builddefs/xap.mk b/builddefs/xap.mk index f08d774297fb..a0c0dc38ed07 100644 --- a/builddefs/xap.mk +++ b/builddefs/xap.mk @@ -1,12 +1,6 @@ # Copyright 2022 Nick Brassel (@tzarc) # SPDX-License-Identifier: GPL-2.0-or-later -# XAP embedded info.json -$(KEYMAP_OUTPUT)/src/info_json_gz.h: $(INFO_JSON_FILES) - @$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD) - $(eval CMD=$(QMK_BIN) xap-generate-info-h -o "$(KEYMAP_OUTPUT)/src/info_json_gz.h" -kb $(KEYBOARD) -km $(KEYMAP)) - @$(BUILD_CMD) - XAP_FILES := $(shell ls -1 data/xap/* | sort | xargs echo) ifneq ("$(wildcard $(KEYBOARD_PATH_1)/xap.hjson)","") XAP_FILES += $(KEYBOARD_PATH_1)/xap.hjson @@ -27,6 +21,11 @@ ifneq ("$(wildcard $(KEYMAP_PATH)/xap.hjson)","") XAP_FILES += $(KEYMAP_PATH)/xap.hjson endif +$(KEYMAP_OUTPUT)/src/info_json_gz.h: $(INFO_JSON_FILES) + @$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD) + $(eval CMD=$(QMK_BIN) xap-generate-info-h -o "$(KEYMAP_OUTPUT)/src/info_json_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)) From f727692fd14b9f9b2043dd8e481a53d7c7dceb3b Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 26 May 2022 18:22:54 +0100 Subject: [PATCH 089/203] Render request/response in XAP docs --- data/templates/xap/docs/route_request.md.j2 | 8 +++ data/templates/xap/docs/route_response.md.j2 | 8 +++ data/templates/xap/docs/routes.md.j2 | 6 +- data/xap/xap_0.0.1.hjson | 2 - data/xap/xap_0.1.0.hjson | 2 - data/xap/xap_0.2.0.hjson | 10 +-- docs/xap_0.0.1.md | 6 +- docs/xap_0.1.0.md | 38 +++++------ docs/xap_0.2.0.md | 66 ++++++++++---------- 9 files changed, 79 insertions(+), 67 deletions(-) create mode 100644 data/templates/xap/docs/route_request.md.j2 create mode 100644 data/templates/xap/docs/route_response.md.j2 diff --git a/data/templates/xap/docs/route_request.md.j2 b/data/templates/xap/docs/route_request.md.j2 new file mode 100644 index 000000000000..e0148c9d15cc --- /dev/null +++ b/data/templates/xap/docs/route_request.md.j2 @@ -0,0 +1,8 @@ +{%- if subroute.request_type == 'struct' -%} +
__Request:__ +{%- for member in subroute.request_struct_members -%} +
{{ " "|safe*4 }}* {{ member.name }}: `{{ member.type }}` +{%- endfor -%} +{%- elif subroute.request_type -%} +
__Request:__ `{{ subroute.request_type }}` +{%- endif -%} \ No newline at end of file diff --git a/data/templates/xap/docs/route_response.md.j2 b/data/templates/xap/docs/route_response.md.j2 new file mode 100644 index 000000000000..a36099937ad8 --- /dev/null +++ b/data/templates/xap/docs/route_response.md.j2 @@ -0,0 +1,8 @@ +{%- if subroute.return_type == 'struct' -%} +
__Response:__ +{%- for member in subroute.return_struct_members -%} +
{{ " "|safe*4 }}* {{ member.name }}: `{{ member.type }}` +{%- endfor -%} +{%- elif subroute.return_type -%} +
__Response:__ `{{ subroute.return_type }}` +{%- endif -%} \ No newline at end of file diff --git a/data/templates/xap/docs/routes.md.j2 b/data/templates/xap/docs/routes.md.j2 index c740ae2f5e1e..1c1e61bd519c 100644 --- a/data/templates/xap/docs/routes.md.j2 +++ b/data/templates/xap/docs/routes.md.j2 @@ -3,10 +3,10 @@ {{ route.description }} {% if route.routes %} -| Name | Route | Tags | Definition | -| -- | -- | -- | -- | +| Name | Route | Tags | Payloads | Description | +| -- | -- | -- | -- | -- | {%- for subid, subroute in route.routes | dictsort %} -| {{ subroute.name }} | `{{ id }} {{ subid }}` | {% if 'secure' == subroute.permissions %}__Secure__{% endif %} | {{ subroute.description.replace('\n', '
') }} | +| {{ subroute.name }} | `{{ id }} {{ subid }}` | {% if 'secure' == subroute.permissions %}__Secure__{% endif %} | {%- include 'route_request.md.j2' -%}{%- include 'route_response.md.j2' -%} | {{ subroute.description.replace('\n', '
') }}| {%- endfor %} {% endif %} {%- endfor %} diff --git a/data/xap/xap_0.0.1.hjson b/data/xap/xap_0.0.1.hjson index d22c54286460..2b6def0ff135 100755 --- a/data/xap/xap_0.0.1.hjson +++ b/data/xap/xap_0.0.1.hjson @@ -223,8 +223,6 @@ * 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}. - * Response: - * `u32` value. ''' return_type: u32 return_purpose: bcd-version diff --git a/data/xap/xap_0.1.0.hjson b/data/xap/xap_0.1.0.hjson index aea65843465f..361a37461e14 100755 --- a/data/xap/xap_0.1.0.hjson +++ b/data/xap/xap_0.1.0.hjson @@ -226,8 +226,6 @@ * 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}. - * Response: - * `u32` value. ''' return_type: u32 return_purpose: bcd-version diff --git a/data/xap/xap_0.2.0.hjson b/data/xap/xap_0.2.0.hjson index 822e5190a08d..6c791ffd429b 100755 --- a/data/xap/xap_0.2.0.hjson +++ b/data/xap/xap_0.2.0.hjson @@ -28,7 +28,7 @@ type: command name: Get Layer Count define: GET_LAYER_COUNT - description: TODO + description: Query maximum number of layers that can be addressed within the keymap. return_type: u8 return_constant: DYNAMIC_KEYMAP_LAYER_COUNT } @@ -36,7 +36,7 @@ type: command name: Get Keycode define: GET_KEYMAP_KEYCODE - description: TODO + description: Query the Keycode at the requested location. request_type: struct request_struct_length: 3 request_struct_members: [ @@ -60,7 +60,7 @@ type: command name: Set Keycode define: SET_KEYMAP_KEYCODE - description: TODO + description: Modify the Keycode at the requested location. permissions: secure request_type: struct request_struct_length: 5 @@ -113,7 +113,7 @@ type: command name: Get Keycode define: GET_ENCODER_KEYCODE - description: TODO + description: Query the Keycode at the requested location. request_type: struct request_struct_length: 3 request_struct_members: [ @@ -138,7 +138,7 @@ name: Set Keycode define: SET_ENCODER_KEYCODE permissions: secure - description: TODO + description: Modify the Keycode at the requested location. request_type: struct request_struct_length: 5 request_struct_members: [ diff --git a/docs/xap_0.0.1.md b/docs/xap_0.0.1.md index 20e6115a7cfa..fe85fddb48e1 100644 --- a/docs/xap_0.0.1.md +++ b/docs/xap_0.0.1.md @@ -75,8 +75,8 @@ This is the primary method for determining if a subsystem has been enabled in th This subsystem is always present, and provides the ability to query information about the XAP protocol of the connected device. -| Name | Route | Tags | Definition | -| -- | -- | -- | -- | -| Version Query | `0x00 0x00` | | 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}.
* Response:
* `u32` value. | +| Name | Route | Tags | Payloads | Description | +| -- | -- | -- | -- | -- | +| Version Query | `0x00 0x00` | |
__Response:__ `u32`| 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}.| diff --git a/docs/xap_0.1.0.md b/docs/xap_0.1.0.md index de21009a9cc4..cd9036cff1e2 100644 --- a/docs/xap_0.1.0.md +++ b/docs/xap_0.1.0.md @@ -88,30 +88,30 @@ This is the primary method for determining if a subsystem has been enabled in th This subsystem is always present, and provides the ability to query information about the XAP protocol of the connected device. -| Name | Route | Tags | Definition | -| -- | -- | -- | -- | -| Version Query | `0x00 0x00` | | 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}.
* Response:
* `u32` value. | -| Capabilities Query | `0x00 0x01` | | XAP subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | -| Enabled subsystem query | `0x00 0x02` | | 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` | | 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 | -| Secure Unlock | `0x00 0x04` | | Initiate secure route unlock sequence | -| Secure Lock | `0x00 0x05` | | Disable secure routes | +| Name | Route | Tags | Payloads | Description | +| -- | -- | -- | -- | -- | +| Version Query | `0x00 0x00` | |
__Response:__ `u32`| 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}.| +| 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

* 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| +| 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 | Definition | -| -- | -- | -- | -- | -| Version Query | `0x01 0x00` | | 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}.
* Response:
* `u32` value. | -| Capabilities Query | `0x01 0x01` | | QMK subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | -| Board identifiers | `0x01 0x02` | | Retrieves the set of identifying information for the board. | -| Board Manufacturer | `0x01 0x03` | | Retrieves the name of the manufacturer | -| Product Name | `0x01 0x04` | | Retrieves the product name | -| info.json length | `0x01 0x05` | | Retrieves the length of info.json | -| info.json | `0x01 0x06` | | Retrieves a chunk of info.json | -| Jump to bootloader | `0x01 0x07` | __Secure__ | 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 | -| info.json | `0x01 0x08` | | Retrieves a unique identifier for the board. | +| Name | Route | Tags | Payloads | Description | +| -- | -- | -- | -- | -- | +| Version Query | `0x01 0x00` | |
__Response:__ `u32`| 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}.| +| 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:__
    * Vendor ID: `u16`
    * Product ID: `u16`
    * Product Version: `u16`
    * 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| +| info.json length | `0x01 0x05` | |
__Response:__ `u32`| Retrieves the length of info.json| +| info.json | `0x01 0x06` | |
__Request:__ `u16`
__Response:__ `u8[32]`| Retrieves a chunk of info.json| +| Jump to bootloader | `0x01 0x07` | __Secure__ |
__Response:__ `u8`| 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| +| info.json | `0x01 0x08` | |
__Response:__ `u32[4]`| Retrieves a unique identifier for the board.| ### Keyboard - `0x02` This subsystem is always present, and reserved for user-specific functionality. No routes are defined by XAP. diff --git a/docs/xap_0.2.0.md b/docs/xap_0.2.0.md index fc8afa4ebf3a..ed2130e34fde 100644 --- a/docs/xap_0.2.0.md +++ b/docs/xap_0.2.0.md @@ -88,30 +88,30 @@ This is the primary method for determining if a subsystem has been enabled in th This subsystem is always present, and provides the ability to query information about the XAP protocol of the connected device. -| Name | Route | Tags | Definition | -| -- | -- | -- | -- | -| Version Query | `0x00 0x00` | | 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}.
* Response:
* `u32` value. | -| Capabilities Query | `0x00 0x01` | | XAP subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | -| Enabled subsystem query | `0x00 0x02` | | 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` | | 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 | -| Secure Unlock | `0x00 0x04` | | Initiate secure route unlock sequence | -| Secure Lock | `0x00 0x05` | | Disable secure routes | +| Name | Route | Tags | Payloads | Description | +| -- | -- | -- | -- | -- | +| Version Query | `0x00 0x00` | |
__Response:__ `u32`| 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}.| +| 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

* 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| +| 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 | Definition | -| -- | -- | -- | -- | -| Version Query | `0x01 0x00` | | 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}.
* Response:
* `u32` value. | -| Capabilities Query | `0x01 0x01` | | QMK subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | -| Board identifiers | `0x01 0x02` | | Retrieves the set of identifying information for the board. | -| Board Manufacturer | `0x01 0x03` | | Retrieves the name of the manufacturer | -| Product Name | `0x01 0x04` | | Retrieves the product name | -| info.json length | `0x01 0x05` | | Retrieves the length of info.json | -| info.json | `0x01 0x06` | | Retrieves a chunk of info.json | -| Jump to bootloader | `0x01 0x07` | __Secure__ | 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 | -| info.json | `0x01 0x08` | | Retrieves a unique identifier for the board. | +| Name | Route | Tags | Payloads | Description | +| -- | -- | -- | -- | -- | +| Version Query | `0x01 0x00` | |
__Response:__ `u32`| 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}.| +| 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:__
    * Vendor ID: `u16`
    * Product ID: `u16`
    * Product Version: `u16`
    * 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| +| info.json length | `0x01 0x05` | |
__Response:__ `u32`| Retrieves the length of info.json| +| info.json | `0x01 0x06` | |
__Request:__ `u16`
__Response:__ `u8[32]`| Retrieves a chunk of info.json| +| Jump to bootloader | `0x01 0x07` | __Secure__ |
__Response:__ `u8`| 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| +| info.json | `0x01 0x08` | |
__Response:__ `u32[4]`| Retrieves a unique identifier for the board.| ### Keyboard - `0x02` This subsystem is always present, and reserved for user-specific functionality. No routes are defined by XAP. @@ -125,30 +125,30 @@ This subsystem is always present, and reserved for user-specific functionality. This subsystem allows for live modifications of the keymap, allowing keys to be reassigned without rebuilding the firmware. -| Name | Route | Tags | Definition | -| -- | -- | -- | -- | -| Capabilities Query | `0x04 0x00` | | Dynamic Keymap subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | -| Get Layer Count | `0x04 0x01` | | TODO | -| Get Keycode | `0x04 0x02` | | TODO | -| Set Keycode | `0x04 0x03` | __Secure__ | TODO | +| Name | Route | Tags | Payloads | Description | +| -- | -- | -- | -- | -- | +| Capabilities Query | `0x04 0x00` | |
__Response:__ `u32`| Dynamic Keymap subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.| +| Get Layer Count | `0x04 0x01` | |
__Response:__ `u8`| Query maximum number of layers that can be addressed within the keymap.| +| Get Keycode | `0x04 0x02` | |
__Request:__
    * Layer: `u8`
    * Row: `u8`
    * Column: `u8`
__Response:__ `u16`| Query the Keycode at the requested location.| +| Set Keycode | `0x04 0x03` | __Secure__ |
__Request:__
    * Layer: `u8`
    * Row: `u8`
    * Column: `u8`
    * Keycode: `u16`| Modify the Keycode at the requested location.| ### Dynamic Encoders - `0x05` This subsystem allows for live modifications of the keymap, allowing encoder functionality to be reassigned without rebuilding the firmware. -| Name | Route | Tags | Definition | -| -- | -- | -- | -- | -| Capabilities Query | `0x05 0x00` | | Dynamic Encoders subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | -| Get Keycode | `0x05 0x02` | | TODO | -| Set Keycode | `0x05 0x03` | __Secure__ | TODO | +| Name | Route | Tags | Payloads | Description | +| -- | -- | -- | -- | -- | +| Capabilities Query | `0x05 0x00` | |
__Response:__ `u32`| Dynamic Encoders subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.| +| Get Keycode | `0x05 0x02` | |
__Request:__
    * Layer: `u8`
    * Encoder: `u8`
    * Clockwise: `u8`
__Response:__ `u16`| Query the Keycode at the requested location.| +| Set Keycode | `0x05 0x03` | __Secure__ |
__Request:__
    * Layer: `u8`
    * Encoder: `u8`
    * Clockwise: `u8`
    * Keycode: `u16`| Modify the Keycode at the requested location.| ### Lighting - `0x06` This subsystem allows for control over the lighting subsystem. -| Name | Route | Tags | Definition | -| -- | -- | -- | -- | -| Capabilities Query | `0x06 0x00` | | Lighting subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. | +| Name | Route | Tags | Payloads | Description | +| -- | -- | -- | -- | -- | +| Capabilities Query | `0x06 0x00` | |
__Response:__ `u32`| Lighting subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.| ## Broadcast messages From d0ed9071a258135c3dd3485d78313e9a4b4cdac8 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Sun, 29 May 2022 08:00:55 +1000 Subject: [PATCH 090/203] Branch point after 2022-05-28 Breaking Change. --- readme.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/readme.md b/readme.md index 5649ddfa097a..63b483c74474 100644 --- a/readme.md +++ b/readme.md @@ -1,3 +1,7 @@ +# This is the `develop` branch! + +See the [Breaking Changes](https://docs.qmk.fm/#/breaking_changes) document for more information. + # Quantum Mechanical Keyboard Firmware [![Current Version](https://img.shields.io/github/tag/qmk/qmk_firmware.svg)](https://github.com/qmk/qmk_firmware/tags) From 73dbb042bfa76451a95d43877ff883270c37009c Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Sun, 29 May 2022 08:15:16 +1000 Subject: [PATCH 091/203] Branch point after 2022-05-28 Breaking Change. --- docs/breaking_changes.md | 4 +++- readme.md | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/breaking_changes.md b/docs/breaking_changes.md index 981512cd4c89..f25529009c86 100644 --- a/docs/breaking_changes.md +++ b/docs/breaking_changes.md @@ -58,7 +58,9 @@ This happens immediately after the previous `develop` branch is merged. * `qmk_firmware` git commands * [ ] `git checkout master` * [ ] `git pull --ff-only` - * [ ] `git checkout -b develop` + * [ ] `git checkout develop` + * [ ] `git pull --ff-only` + * [ ] `git merge -no-ff master` * [ ] Edit `readme.md` * [ ] Add a big notice at the top that this is a testing branch. * [ ] Include a link to this document diff --git a/readme.md b/readme.md index 5649ddfa097a..63b483c74474 100644 --- a/readme.md +++ b/readme.md @@ -1,3 +1,7 @@ +# This is the `develop` branch! + +See the [Breaking Changes](https://docs.qmk.fm/#/breaking_changes) document for more information. + # Quantum Mechanical Keyboard Firmware [![Current Version](https://img.shields.io/github/tag/qmk/qmk_firmware.svg)](https://github.com/qmk/qmk_firmware/tags) From d9234837691d39e68111517cdba8bf0783c9646c Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 31 May 2022 18:43:21 +0100 Subject: [PATCH 092/203] Bodge for breakpoint branch name --- lib/python/qmk/xap/gen_firmware/header_generator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/python/qmk/xap/gen_firmware/header_generator.py b/lib/python/qmk/xap/gen_firmware/header_generator.py index bd54c93720d2..639b91b3a38f 100755 --- a/lib/python/qmk/xap/gen_firmware/header_generator.py +++ b/lib/python/qmk/xap/gen_firmware/header_generator.py @@ -245,8 +245,8 @@ def generate_header(output_file, keyboard, keymap): 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.match(git_get_version() or "") - lines.append(f'#define QMK_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('') From ff96f67e92c0f9af2e09f51fbaf92d93bdcace8d Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 2 Jun 2022 20:16:48 +0100 Subject: [PATCH 093/203] format --- lib/python/qmk/xap/gen_firmware/header_generator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/python/qmk/xap/gen_firmware/header_generator.py b/lib/python/qmk/xap/gen_firmware/header_generator.py index 639b91b3a38f..423e9e65d36e 100755 --- a/lib/python/qmk/xap/gen_firmware/header_generator.py +++ b/lib/python/qmk/xap/gen_firmware/header_generator.py @@ -245,7 +245,7 @@ def generate_header(output_file, keyboard, keymap): 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')] + 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') From d255c2f57fe3759d714c040c0129baba382dce23 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Sun, 5 Jun 2022 08:52:01 +1000 Subject: [PATCH 094/203] Fixup casing. --- data/constants/keycodes_0.0.1.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/constants/keycodes_0.0.1.json b/data/constants/keycodes_0.0.1.json index 7644ba223b6b..f3ff0166e3cd 100644 --- a/data/constants/keycodes_0.0.1.json +++ b/data/constants/keycodes_0.0.1.json @@ -494,7 +494,7 @@ }, "0x0052": { "group": "basic", - "key": "KC_Up", + "key": "KC_UP", "label": "Up" }, "0x0053": { From ba7770a4e41197ff9a8533577d836351e3263f8b Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 16 Jun 2022 14:25:14 +0100 Subject: [PATCH 095/203] Fix secure symbol missing --- quantum/xap/xap_handlers.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/quantum/xap/xap_handlers.c b/quantum/xap/xap_handlers.c index 52b74cc2b0b6..a104c5c65bb0 100644 --- a/quantum/xap/xap_handlers.c +++ b/quantum/xap/xap_handlers.c @@ -89,8 +89,7 @@ bool xap_respond_secure_lock(xap_token_t token, const void *data, size_t length) #ifdef BOOTLOADER_JUMP_SUPPORTED bool xap_respond_request_bootloader_jump(xap_token_t token, const void *data, size_t length) { - extern uint8_t secure_status; - uint8_t ret = secure_status == 2; + 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)); From 956bd3b7ca9598dfd722b298ed5e50f9d2b28294 Mon Sep 17 00:00:00 2001 From: zvecr Date: Sun, 19 Jun 2022 22:04:30 +0100 Subject: [PATCH 096/203] Refactor based on recent discussion --- builddefs/xap.mk | 6 +- data/xap/xap_0.1.0.hjson | 18 ++-- data/xap/xap_0.2.0.hjson | 83 +++++++++---------- docs/xap_0.1.0.md | 6 +- docs/xap_0.2.0.md | 27 +++--- lib/python/qmk/cli/xap/generate_qmk.py | 10 +-- lib/python/qmk/cli/xap/xap.py | 26 +++--- .../{info_generator.py => blob_generator.py} | 6 +- quantum/xap/xap.c | 10 +-- quantum/xap/xap_handlers.c | 59 +++++++------ 10 files changed, 125 insertions(+), 126 deletions(-) rename lib/python/qmk/xap/gen_firmware/{info_generator.py => blob_generator.py} (84%) diff --git a/builddefs/xap.mk b/builddefs/xap.mk index a0c0dc38ed07..14c3ada9f4be 100644 --- a/builddefs/xap.mk +++ b/builddefs/xap.mk @@ -21,9 +21,9 @@ ifneq ("$(wildcard $(KEYMAP_PATH)/xap.hjson)","") XAP_FILES += $(KEYMAP_PATH)/xap.hjson endif -$(KEYMAP_OUTPUT)/src/info_json_gz.h: $(INFO_JSON_FILES) +$(KEYMAP_OUTPUT)/src/config_blob_gz.h: $(INFO_JSON_FILES) @$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD) - $(eval CMD=$(QMK_BIN) xap-generate-info-h -o "$(KEYMAP_OUTPUT)/src/info_json_gz.h" -kb $(KEYBOARD) -km $(KEYMAP)) + $(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) @@ -36,6 +36,6 @@ $(KEYMAP_OUTPUT)/src/xap_generated.h: $(XAP_FILES) $(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/info_json_gz.h $(KEYMAP_OUTPUT)/src/xap_generated.inl $(KEYMAP_OUTPUT)/src/xap_generated.h +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 diff --git a/data/xap/xap_0.1.0.hjson b/data/xap/xap_0.1.0.hjson index 361a37461e14..8965f2b51363 100755 --- a/data/xap/xap_0.1.0.hjson +++ b/data/xap/xap_0.1.0.hjson @@ -296,21 +296,21 @@ } 0x05: { type: command - name: info.json length - define: INFO_LEN_QUERY - description: Retrieves the length of info.json + 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: INFO_JSON_GZ_LEN + return_constant: CONFIG_BLOB_GZ_LEN } 0x06: { type: command - name: info.json - define: INFO_QUERY - description: Retrieves a chunk of info.json + 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_info_json_chunk + return_execute: get_config_blob_chunk } 0x07: { type: command @@ -332,7 +332,7 @@ } 0x08: { type: command - name: info.json + name: Unique Identifier define: HARDWARE_ID description: Retrieves a unique identifier for the board. return_type: u32[4] diff --git a/data/xap/xap_0.2.0.hjson b/data/xap/xap_0.2.0.hjson index 6c791ffd429b..20118d93f414 100755 --- a/data/xap/xap_0.2.0.hjson +++ b/data/xap/xap_0.2.0.hjson @@ -4,33 +4,24 @@ routes: { 0x04: { type: router - name: Dynamic Keymap - define: DYNAMIC_KEYMAP + name: Keymap + define: KEYMAP description: ''' - This subsystem allows for live modifications of the keymap, allowing keys to be reassigned without rebuilding the firmware. + This subsystem allows for query of currently configured keycodes. ''' - enable_if_preprocessor: defined(DYNAMIC_KEYMAP_ENABLE) routes: { - 0x00: { + 0x01: { type: command name: Capabilities Query define: CAPABILITIES_QUERY description: ''' - Dynamic Keymap subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. + 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_DYNAMIC_KEYMAP_CAPABILITIES - } - 0x01: { - 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_constant: DYNAMIC_KEYMAP_LAYER_COUNT + return_constant: XAP_ROUTE_KEYMAP_CAPABILITIES } 0x02: { type: command @@ -54,16 +45,16 @@ } ] return_type: u16 - return_execute: dynamic_keymap_get_keycode + return_execute: get_keymap_keycode } 0x03: { type: command - name: Set Keycode - define: SET_KEYMAP_KEYCODE - description: Modify the Keycode at the requested location. - permissions: secure + 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: 5 + request_struct_length: 3 request_struct_members: [ { type: u8 @@ -71,51 +62,49 @@ }, { type: u8 - name: Row + name: Encoder }, { type: u8 - name: Column - }, - { - type: u16 - name: Keycode + name: Clockwise } ] - return_execute: dynamic_keymap_set_keycode + return_type: u16 + return_execute: get_encoder_keycode } } } 0x05: { type: router - name: Dynamic Encoders - define: DYNAMIC_ENCODER + name: Remapping + define: REMAPPING description: ''' - This subsystem allows for live modifications of the keymap, allowing encoder functionality to be reassigned without rebuilding the firmware. + This subsystem allows for live reassignment of keycodes without rebuilding the firmware. ''' - enable_if_preprocessor: defined(DYNAMIC_KEYMAP_ENABLE) && defined(ENCODER_MAP_ENABLE) + enable_if_preprocessor: defined(DYNAMIC_KEYMAP_ENABLE) routes: { - 0x00: { + 0x01: { type: command name: Capabilities Query define: CAPABILITIES_QUERY description: ''' - Dynamic Encoders subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem. + 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_DYNAMIC_ENCODER_CAPABILITIES + return_constant: XAP_ROUTE_REMAPPING_CAPABILITIES } 0x02: { type: command - name: Get Keycode - define: GET_ENCODER_KEYCODE - description: Query the Keycode at the requested location. + name: Set Keycode + define: SET_KEYMAP_KEYCODE + description: Modify the Keycode at the requested location. + permissions: secure request_type: struct - request_struct_length: 3 + request_struct_length: 5 request_struct_members: [ { type: u8 @@ -123,22 +112,26 @@ }, { type: u8 - name: Encoder + name: Row }, { type: u8 - name: Clockwise + name: Column + }, + { + type: u16 + name: Keycode } ] - return_type: u16 - return_execute: dynamic_encoder_get_keycode + return_execute: dynamic_keymap_set_keycode } 0x03: { type: command - name: Set Keycode + 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: [ @@ -174,7 +167,7 @@ ''' enable_if_preprocessor: defined(RGBLIGHT_ENABLE) || defined(RGB_MATRIX_ENABLE) routes: { - 0x00: { + 0x01: { type: command name: Capabilities Query define: CAPABILITIES_QUERY diff --git a/docs/xap_0.1.0.md b/docs/xap_0.1.0.md index cd9036cff1e2..8c54aff882c6 100644 --- a/docs/xap_0.1.0.md +++ b/docs/xap_0.1.0.md @@ -108,10 +108,10 @@ This subsystem is always present, and provides the ability to address QMK-specif | Board identifiers | `0x01 0x02` | |
__Response:__
    * Vendor ID: `u16`
    * Product ID: `u16`
    * Product Version: `u16`
    * 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| -| info.json length | `0x01 0x05` | |
__Response:__ `u32`| Retrieves the length of info.json| -| info.json | `0x01 0x06` | |
__Request:__ `u16`
__Response:__ `u8[32]`| Retrieves a chunk of info.json| +| 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`
__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

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| -| info.json | `0x01 0x08` | |
__Response:__ `u32[4]`| Retrieves a unique identifier for the board.| +| Unique Identifier | `0x01 0x08` | |
__Response:__ `u32[4]`| Retrieves a unique identifier for the board.| ### Keyboard - `0x02` This subsystem is always present, and reserved for user-specific functionality. No routes are defined by XAP. diff --git a/docs/xap_0.2.0.md b/docs/xap_0.2.0.md index ed2130e34fde..e1d1a0882b26 100644 --- a/docs/xap_0.2.0.md +++ b/docs/xap_0.2.0.md @@ -108,10 +108,10 @@ This subsystem is always present, and provides the ability to address QMK-specif | Board identifiers | `0x01 0x02` | |
__Response:__
    * Vendor ID: `u16`
    * Product ID: `u16`
    * Product Version: `u16`
    * 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| -| info.json length | `0x01 0x05` | |
__Response:__ `u32`| Retrieves the length of info.json| -| info.json | `0x01 0x06` | |
__Request:__ `u16`
__Response:__ `u8[32]`| Retrieves a chunk of info.json| +| 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`
__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

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| -| info.json | `0x01 0x08` | |
__Response:__ `u32[4]`| Retrieves a unique identifier for the board.| +| Unique Identifier | `0x01 0x08` | |
__Response:__ `u32[4]`| Retrieves a unique identifier for the board.| ### Keyboard - `0x02` This subsystem is always present, and reserved for user-specific functionality. No routes are defined by XAP. @@ -121,26 +121,25 @@ This subsystem is always present, and reserved for user-specific functionality. This subsystem is always present, and reserved for user-specific functionality. No routes are defined by XAP. -### Dynamic Keymap - `0x04` -This subsystem allows for live modifications of the keymap, allowing keys to be reassigned without rebuilding the firmware. +### Keymap - `0x04` +This subsystem allows for query of currently configured keycodes. | Name | Route | Tags | Payloads | Description | | -- | -- | -- | -- | -- | -| Capabilities Query | `0x04 0x00` | |
__Response:__ `u32`| Dynamic Keymap subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.| -| Get Layer Count | `0x04 0x01` | |
__Response:__ `u8`| Query maximum number of layers that can be addressed within the keymap.| +| Capabilities Query | `0x04 0x01` | |
__Response:__ `u32`| Keymap subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.| | Get Keycode | `0x04 0x02` | |
__Request:__
    * Layer: `u8`
    * Row: `u8`
    * Column: `u8`
__Response:__ `u16`| Query the Keycode at the requested location.| -| Set Keycode | `0x04 0x03` | __Secure__ |
__Request:__
    * Layer: `u8`
    * Row: `u8`
    * Column: `u8`
    * Keycode: `u16`| Modify the Keycode at the requested location.| +| Get Encoder Keycode | `0x04 0x03` | |
__Request:__
    * Layer: `u8`
    * Encoder: `u8`
    * Clockwise: `u8`
__Response:__ `u16`| Query the Keycode at the requested location.| -### Dynamic Encoders - `0x05` -This subsystem allows for live modifications of the keymap, allowing encoder functionality to be reassigned without rebuilding the firmware. +### Remapping - `0x05` +This subsystem allows for live reassignment of keycodes without rebuilding the firmware. | Name | Route | Tags | Payloads | Description | | -- | -- | -- | -- | -- | -| Capabilities Query | `0x05 0x00` | |
__Response:__ `u32`| Dynamic Encoders subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.| -| Get Keycode | `0x05 0x02` | |
__Request:__
    * Layer: `u8`
    * Encoder: `u8`
    * Clockwise: `u8`
__Response:__ `u16`| Query the Keycode at the requested location.| -| Set Keycode | `0x05 0x03` | __Secure__ |
__Request:__
    * Layer: `u8`
    * Encoder: `u8`
    * Clockwise: `u8`
    * Keycode: `u16`| Modify the Keycode at the requested location.| +| Capabilities Query | `0x05 0x01` | |
__Response:__ `u32`| Remapping subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.| +| Set Keycode | `0x05 0x02` | __Secure__ |
__Request:__
    * Layer: `u8`
    * Row: `u8`
    * Column: `u8`
    * Keycode: `u16`| Modify the Keycode at the requested location.| +| Set Encoder Keycode | `0x05 0x03` | __Secure__ |
__Request:__
    * Layer: `u8`
    * Encoder: `u8`
    * Clockwise: `u8`
    * Keycode: `u16`| Modify the Keycode at the requested location.| ### Lighting - `0x06` This subsystem allows for control over the lighting subsystem. @@ -148,7 +147,7 @@ This subsystem allows for control over the lighting subsystem. | Name | Route | Tags | Payloads | Description | | -- | -- | -- | -- | -- | -| Capabilities Query | `0x06 0x00` | |
__Response:__ `u32`| Lighting subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.| +| Capabilities Query | `0x06 0x01` | |
__Response:__ `u32`| Lighting subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.| ## Broadcast messages diff --git a/lib/python/qmk/cli/xap/generate_qmk.py b/lib/python/qmk/cli/xap/generate_qmk.py index d4ebb7e9dbde..5144d5fd7224 100755 --- a/lib/python/qmk/cli/xap/generate_qmk.py +++ b/lib/python/qmk/cli/xap/generate_qmk.py @@ -4,7 +4,7 @@ from qmk.path import normpath from qmk.keyboard import keyboard_completer, keyboard_folder -from qmk.xap.gen_firmware.info_generator import generate_info +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 @@ -52,9 +52,9 @@ def xap_generate_qmk_h(cli): @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 info.json payload include.', hidden=False if cli.config.user.developer else True) -def xap_generate_info_h(cli): - """Generates the XAP info.json payload header file, generated during normal build. +@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: @@ -66,4 +66,4 @@ def xap_generate_info_h(cli): cli.subcommands['xap-generate-info-h'].print_help() return False - generate_info(cli.args.output, cli.args.keyboard, cli.args.keymap) + generate_blob(cli.args.output, cli.args.keyboard, cli.args.keymap) diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index 8de1b5680827..272f667a1205 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -163,23 +163,23 @@ def _list_devices(): print_dotted_output(data) -def xap_dummy(device): - # get layer count - layers = _xap_transaction(device, 0x04, 0x01) - layers = int.from_bytes(layers, "little") - print(f'layers:{layers}') +# def xap_dummy(device): +# # get layer count +# layers = _xap_transaction(device, 0x04, 0x00) +# layers = int.from_bytes(layers, "little") +# print(f'layers:{layers}') - # get keycode [layer:0, row:0, col:0] - # keycode = _xap_transaction(device, 0x04, 0x02, b"\x00\x00\x00") +# # get keycode [layer:0, row:0, col:0] +# # keycode = _xap_transaction(device, 0x04, 0x02, b"\x00\x00\x00") - # get encoder [layer:0, index:0, clockwise:0] - keycode = _xap_transaction(device, 0x05, 0x02, b"\x00\x00\x00") +# # get encoder [layer:0, index:0, clockwise:0] +# keycode = _xap_transaction(device, 0x04, 0x03, b"\x00\x00\x00") - keycode = int.from_bytes(keycode, "little") - print(f'keycode:{KEYCODE_MAP.get(keycode, "unknown")}[{keycode}]') +# keycode = int.from_bytes(keycode, "little") +# print(f'keycode:{KEYCODE_MAP.get(keycode, "unknown")}[{keycode}]') - # set encoder [layer:0, index:0, clockwise:0, keycode:KC_A] - _xap_transaction(device, 0x05, 0x03, b"\x00\x00\x00\x04\00") +# # set encoder [layer:0, index:0, clockwise:0, keycode:KC_A] +# _xap_transaction(device, 0x05, 0x03, b"\x00\x00\x00\x04\00") def xap_broadcast_listen(device): diff --git a/lib/python/qmk/xap/gen_firmware/info_generator.py b/lib/python/qmk/xap/gen_firmware/blob_generator.py similarity index 84% rename from lib/python/qmk/xap/gen_firmware/info_generator.py rename to lib/python/qmk/xap/gen_firmware/blob_generator.py index a1a6f1b40c61..13e19688dfa5 100644 --- a/lib/python/qmk/xap/gen_firmware/info_generator.py +++ b/lib/python/qmk/xap/gen_firmware/blob_generator.py @@ -9,7 +9,7 @@ from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE -def generate_info(output_file, keyboard, keymap): +def generate_blob(output_file, keyboard, keymap): # Build the info.json file km_info_json = keymap_json(keyboard, keymap) @@ -33,9 +33,9 @@ def generate_info(output_file, keyboard, keymap): lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', ''] # Gen output file - lines.append('static const unsigned char info_json_gz[] PROGMEM = {') + lines.append('static const unsigned char config_blob_gz[] PROGMEM = {') lines.append(data) lines.append('};') - lines.append(f'#define INFO_JSON_GZ_LEN {data_len}') + lines.append(f'#define CONFIG_BLOB_GZ_LEN {data_len}') dump_lines(output_file, lines) diff --git a/quantum/xap/xap.c b/quantum/xap/xap.c index 52030639edd9..83bdbe13f396 100644 --- a/quantum/xap/xap.c +++ b/quantum/xap/xap.c @@ -18,13 +18,13 @@ #include #include "secure.h" -#include "info_json_gz.h" -bool get_info_json_chunk(uint16_t offset, uint8_t *data, uint8_t data_len) { - if (offset + data_len > INFO_JSON_GZ_LEN) { - data_len = INFO_JSON_GZ_LEN - offset; +#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, &info_json_gz[offset], data_len); + memcpy_P(data, &config_blob_gz[offset], data_len); return true; } diff --git a/quantum/xap/xap_handlers.c b/quantum/xap/xap_handlers.c index a104c5c65bb0..bd9cda148bea 100644 --- a/quantum/xap/xap_handlers.c +++ b/quantum/xap/xap_handlers.c @@ -53,16 +53,17 @@ uint32_t xap_route_qmk_ffffffffffffffff_getter(void) { return 0x12345678; } -bool xap_respond_get_info_json_chunk(xap_token_t token, const void *data, size_t length) { +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_info_query_t ret = {0}; + uint16_t offset = *((uint16_t *)data); - bool get_info_json_chunk(uint16_t offset, uint8_t * data, uint8_t data_len); - get_info_json_chunk(offset, (uint8_t *)&ret, sizeof(ret)); + 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)); } @@ -89,7 +90,7 @@ bool xap_respond_secure_lock(xap_token_t token, const void *data, size_t length) #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(); + 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)); @@ -103,52 +104,58 @@ bool xap_respond_get_hardware_id(xap_token_t token, const void *data, size_t len return xap_respond_data(token, &ret, sizeof(ret)); } -#if ((defined(DYNAMIC_KEYMAP_ENABLE))) -bool xap_respond_dynamic_keymap_get_keycode(xap_token_t token, const void *data, size_t length) { - if (length != sizeof(xap_route_dynamic_keymap_get_keymap_keycode_arg_t)) { +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_dynamic_keymap_get_keymap_keycode_arg_t *arg = (xap_route_dynamic_keymap_get_keymap_keycode_arg_t *)data; + 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 = dynamic_keymap_get_keycode(arg->layer, arg->row, arg->column); + uint16_t keycode = keymap_key_to_keycode(arg->layer, pos); return xap_respond_data(token, &keycode, sizeof(keycode)); } -bool xap_respond_dynamic_keymap_set_keycode(xap_token_t token, const void *data, size_t length) { - if (length != sizeof(xap_route_dynamic_keymap_set_keymap_keycode_arg_t)) { +#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_dynamic_keymap_set_keymap_keycode_arg_t *arg = (xap_route_dynamic_keymap_set_keymap_keycode_arg_t *)data; + xap_route_keymap_get_encoder_keycode_arg_t *arg = (xap_route_keymap_get_encoder_keycode_arg_t *)data; - dynamic_keymap_set_keycode(arg->layer, arg->row, arg->column, arg->keycode); - xap_respond_success(token); - return true; + 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(ENCODER_MAP_ENABLE))) -bool xap_respond_dynamic_encoder_get_keycode(xap_token_t token, const void *data, size_t length) { - if (length != sizeof(xap_route_dynamic_encoder_get_encoder_keycode_arg_t)) { +#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_dynamic_encoder_get_encoder_keycode_arg_t *arg = (xap_route_dynamic_encoder_get_encoder_keycode_arg_t *)data; + xap_route_remapping_set_keymap_keycode_arg_t *arg = (xap_route_remapping_set_keymap_keycode_arg_t *)data; - uint16_t keycode = dynamic_keymap_get_encoder(arg->layer, arg->encoder, arg->clockwise); - return xap_respond_data(token, &keycode, sizeof(keycode)); + 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_dynamic_encoder_set_encoder_keycode_arg_t)) { + if (length != sizeof(xap_route_remapping_set_encoder_keycode_arg_t)) { return false; } - xap_route_dynamic_encoder_set_encoder_keycode_arg_t *arg = (xap_route_dynamic_encoder_set_encoder_keycode_arg_t *)data; + 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 \ No newline at end of file +#endif From dab3a4fde0fd50673ee06f6cab322a84b894062f Mon Sep 17 00:00:00 2001 From: zvecr Date: Sun, 19 Jun 2022 23:26:27 +0100 Subject: [PATCH 097/203] Refactor based on recent discussion - Add back 'Layer Count' --- data/xap/xap_0.2.0.hjson | 20 ++++++++++++++++++-- docs/xap_0.2.0.md | 10 ++++++---- lib/python/qmk/cli/xap/xap.py | 14 +++++++------- quantum/xap/xap_handlers.c | 5 +++++ 4 files changed, 36 insertions(+), 13 deletions(-) diff --git a/data/xap/xap_0.2.0.hjson b/data/xap/xap_0.2.0.hjson index 20118d93f414..245fa3e4ed75 100755 --- a/data/xap/xap_0.2.0.hjson +++ b/data/xap/xap_0.2.0.hjson @@ -24,6 +24,14 @@ 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 @@ -47,7 +55,7 @@ return_type: u16 return_execute: get_keymap_keycode } - 0x03: { + 0x04: { type: command name: Get Encoder Keycode define: GET_ENCODER_KEYCODE @@ -98,6 +106,14 @@ 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 @@ -125,7 +141,7 @@ ] return_execute: dynamic_keymap_set_keycode } - 0x03: { + 0x04: { type: command name: Set Encoder Keycode define: SET_ENCODER_KEYCODE diff --git a/docs/xap_0.2.0.md b/docs/xap_0.2.0.md index e1d1a0882b26..30f51dd863c2 100644 --- a/docs/xap_0.2.0.md +++ b/docs/xap_0.2.0.md @@ -128,8 +128,9 @@ 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 Keycode | `0x04 0x02` | |
__Request:__
    * Layer: `u8`
    * Row: `u8`
    * Column: `u8`
__Response:__ `u16`| Query the Keycode at the requested location.| -| Get Encoder Keycode | `0x04 0x03` | |
__Request:__
    * Layer: `u8`
    * Encoder: `u8`
    * Clockwise: `u8`
__Response:__ `u16`| Query the Keycode at the requested location.| +| Get Layer Count | `0x04 0x02` | |
__Response:__ `u8`| Query maximum number of layers that can be addressed within the keymap.| +| Get Keycode | `0x04 0x03` | |
__Request:__
    * Layer: `u8`
    * Row: `u8`
    * Column: `u8`
__Response:__ `u16`| Query the Keycode at the requested location.| +| Get Encoder Keycode | `0x04 0x04` | |
__Request:__
    * Layer: `u8`
    * Encoder: `u8`
    * Clockwise: `u8`
__Response:__ `u16`| Query the Keycode at the requested location.| ### Remapping - `0x05` This subsystem allows for live reassignment of keycodes without rebuilding the firmware. @@ -138,8 +139,9 @@ This subsystem allows for live reassignment of keycodes without rebuilding the f | 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.| -| Set Keycode | `0x05 0x02` | __Secure__ |
__Request:__
    * Layer: `u8`
    * Row: `u8`
    * Column: `u8`
    * Keycode: `u16`| Modify the Keycode at the requested location.| -| Set Encoder Keycode | `0x05 0x03` | __Secure__ |
__Request:__
    * Layer: `u8`
    * Encoder: `u8`
    * Clockwise: `u8`
    * Keycode: `u16`| Modify the Keycode at the requested location.| +| 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:__
    * Layer: `u8`
    * Row: `u8`
    * Column: `u8`
    * Keycode: `u16`| Modify the Keycode at the requested location.| +| Set Encoder Keycode | `0x05 0x04` | __Secure__ |
__Request:__
    * Layer: `u8`
    * Encoder: `u8`
    * Clockwise: `u8`
    * Keycode: `u16`| Modify the Keycode at the requested location.| ### Lighting - `0x06` This subsystem allows for control over the lighting subsystem. diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index 272f667a1205..2de6663684bd 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -165,21 +165,21 @@ def _list_devices(): # def xap_dummy(device): # # get layer count -# layers = _xap_transaction(device, 0x04, 0x00) +# layers = _xap_transaction(device, 0x04, 0x02) # layers = int.from_bytes(layers, "little") # print(f'layers:{layers}') # # get keycode [layer:0, row:0, col:0] -# # keycode = _xap_transaction(device, 0x04, 0x02, b"\x00\x00\x00") +# # keycode = _xap_transaction(device, 0x04, 0x03, b"\x00\x00\x00") # # get encoder [layer:0, index:0, clockwise:0] -# keycode = _xap_transaction(device, 0x04, 0x03, b"\x00\x00\x00") +# keycode = _xap_transaction(device, 0x04, 0x04, b"\x00\x00\x00") # keycode = int.from_bytes(keycode, "little") # print(f'keycode:{KEYCODE_MAP.get(keycode, "unknown")}[{keycode}]') # # set encoder [layer:0, index:0, clockwise:0, keycode:KC_A] -# _xap_transaction(device, 0x05, 0x03, b"\x00\x00\x00\x04\00") +# _xap_transaction(device, 0x05, 0x04, b"\x00\x00\x00\x04\00") def xap_broadcast_listen(device): @@ -235,7 +235,7 @@ def do_keycode(self, arg): cli.log.error("Invalid args") return - keycode = _xap_transaction(self.device, 0x04, 0x02, data) + keycode = _xap_transaction(self.device, 0x04, 0x03, data) keycode = int.from_bytes(keycode, "little") print(f'keycode:{self.keycodes.get(keycode, "unknown")}[{keycode}]') @@ -254,7 +254,7 @@ def do_keymap(self, arg): 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 = _xap_transaction(self.device, 0x04, 0x02, q) + keycode = _xap_transaction(self.device, 0x04, 0x03, q) keycode = int.from_bytes(keycode, "little") print(f'| {self.keycodes.get(keycode, "unknown").ljust(7)} ', end='', flush=True) print('|') @@ -276,7 +276,7 @@ def do_layer(self, arg): keycodes = [] for item in layout: q = data + bytes(item['matrix']) - keycode = _xap_transaction(self.device, 0x04, 0x02, q) + keycode = _xap_transaction(self.device, 0x04, 0x03, q) keycode = int.from_bytes(keycode, "little") keycodes.append(self.keycodes.get(keycode, "???")) diff --git a/quantum/xap/xap_handlers.c b/quantum/xap/xap_handlers.c index bd9cda148bea..8ae9bcedf511 100644 --- a/quantum/xap/xap_handlers.c +++ b/quantum/xap/xap_handlers.c @@ -104,6 +104,11 @@ bool xap_respond_get_hardware_id(xap_token_t token, const void *data, size_t len 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; From 90fc9016241db3ccc12138f84e1fcf95a389838d Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 21 Jun 2022 13:27:45 +0100 Subject: [PATCH 098/203] mermaid? --- docs/index.html | 3 +++ docs/xap.md | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/index.html b/docs/index.html index 4827024bdc7b..264783e12f69 100644 --- a/docs/index.html +++ b/docs/index.html @@ -128,6 +128,8 @@ + + @@ -143,5 +145,6 @@ navigator.serviceWorker.register('sw.js') } + diff --git a/docs/xap.md b/docs/xap.md index 340c42bca836..f83af10618ee 100644 --- a/docs/xap.md +++ b/docs/xap.md @@ -4,7 +4,13 @@ XAP (“extensible application protocol”) API intends to provide access to var ## Overview -TODO +```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 From c22fedb5b292a215e52594c3247333d595c62cfa Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 21 Jun 2022 13:27:53 +0100 Subject: [PATCH 099/203] Refactor xap client --- lib/python/qmk/cli/xap/xap.py | 357 +++++++++++++++++++--------------- 1 file changed, 198 insertions(+), 159 deletions(-) diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index 2de6663684bd..2784a631bbfb 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -4,6 +4,9 @@ import json import random import gzip +import threading +import functools +from enum import IntFlag from platform import platform from milc import cli @@ -14,21 +17,179 @@ KEYCODE_MAP = get_xap_keycodes('latest') -def _is_xap_usage(x): - return x['usage_page'] == 0xFF51 and x['usage'] == 0x0058 +def _u32toBCD(val): # noqa: N802 + """Create BCD string + """ + return f'{val>>24}.{val>>16 & 0xFF}.{val & 0xFFFF}' + +class XAPFlags(IntFlag): + SUCCESS = 0x01 -def _is_filtered_device(x): - name = "%04x:%04x" % (x['vendor_id'], x['product_id']) - return name.lower().startswith(cli.args.device.lower()) +class XAPDevice: + def __init__(self, dev): + """Constructor opens hid device and starts dependent services + """ + self.responses = {} + + self.dev = hid.Device(path=dev['path']) -def _search(): - devices = filter(_is_xap_usage, hid.enumerate()) - if cli.args.device: - devices = filter(_is_filtered_device, devices) + 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 = str(array_alpha[:2]) + event = self.responses.get(token) + if event: + event._ret = array_alpha + event.set() + + def _query_device_info(self): + datalen = int.from_bytes(self.transaction(0x01, 0x05) or bytes(0), "little") + if not datalen: + return {} + + data = [] + offset = 0 + while offset < datalen: + chunk = self.transaction(0x01, 0x06, 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 = b"\xFF\xFF" + event = threading.Event() + self.responses[str(token)] = event + + event.wait() + return event._ret + + def transaction(self, sub, route, *args): + """Request/Receive + """ + # token cannot start with zero or be FFFF + token = random.randrange(0x0100, 0xFFFE).to_bytes(2, byteorder='big') + + # send with padding + # TODO: this code is total garbage + args_data = [] + args_len = 2 + if len(args) == 1: + if isinstance(args[0], (bytes, bytearray)): + args_len += len(args[0]) + args_data = args[0] + else: + args_len += 2 + args_data = args[0].to_bytes(2, byteorder='little') + + padding_len = 64 - 3 - args_len + padding = b"\x00" * padding_len + if args_data: + padding = args_data + padding + buffer = token + args_len.to_bytes(1, byteorder='little') + sub.to_bytes(1, byteorder='little') + route.to_bytes(1, byteorder='little') + padding + + # prepend 0 on windows because reasons... + if 'windows' in platform().lower(): + buffer = b"\x00" + buffer + + event = threading.Event() + self.responses[str(token)] = event + + self.dev.write(buffer) + event.wait(timeout=1) + self.responses.pop(str(token), None) + if not hasattr(event, '_ret'): + return None + + array_alpha = event._ret + if int(array_alpha[2]) != XAPFlags.SUCCESS: + return None + + payload_len = int(array_alpha[3]) + return array_alpha[4:4 + payload_len] + + @functools.cache + def version(self): + ver = int.from_bytes(self.transaction(0x00, 0x00) or bytes(0), 'little') + return {'xap': _u32toBCD(ver)} + + @functools.cache + def info(self): + data = self._query_device_info() + data['_id'] = self.transaction(0x01, 0x08) + data['xap'] = self.version()['xap'] + return data + + def unlock(self): + self.transaction(0x00, 0x04) + + +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() - return list(devices) + 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) + + +# def _query_device_secure(device): +# secure = int.from_bytes(_xap_transaction(device, 0x00, 0x03), 'little') +# secure = 'unlocked' if secure == 2 else 'LOCKED' +# return {'secure': secure} +# +# def xap_dummy(device): +# # get layer count +# layers = _xap_transaction(device, 0x04, 0x02) +# layers = int.from_bytes(layers, "little") +# print(f'layers:{layers}') + +# # get keycode [layer:0, row:0, col:0] +# # keycode = _xap_transaction(device, 0x04, 0x03, b"\x00\x00\x00") + +# # get encoder [layer:0, index:0, clockwise:0] +# keycode = _xap_transaction(device, 0x04, 0x04, b"\x00\x00\x00") + +# keycode = int.from_bytes(keycode, "little") +# print(f'keycode:{KEYCODE_MAP.get(keycode, "unknown")}[{keycode}]') + +# # set encoder [layer:0, index:0, clockwise:0, keycode:KC_A] +# _xap_transaction(device, 0x05, 0x04, b"\x00\x00\x00\x04\00") def print_dotted_output(kb_info_json, prefix=''): @@ -52,154 +213,27 @@ def print_dotted_output(kb_info_json, prefix=''): 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(sorted(map(str, data)))) + 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 _xap_transaction(device, sub, route, *args): - # gen token - tok = random.getrandbits(16) - token = tok.to_bytes(2, byteorder='little') - - # send with padding - # TODO: this code is total garbage - args_data = [] - args_len = 2 - if len(args) == 1: - if isinstance(args[0], (bytes, bytearray)): - args_len += len(args[0]) - args_data = args[0] - else: - args_len += 2 - args_data = args[0].to_bytes(2, byteorder='little') - - padding_len = 64 - 3 - args_len - padding = b"\x00" * padding_len - if args_data: - padding = args_data + padding - buffer = token + args_len.to_bytes(1, byteorder='little') + sub.to_bytes(1, byteorder='little') + route.to_bytes(1, byteorder='little') + padding - - # prepend 0 on windows because reasons... - if 'windows' in platform().lower(): - buffer = b"\x00" + buffer - - device.write(buffer) - - # get resp - array_alpha = device.read(64, 250) - - # validate tok sent == resp - if str(token) != str(array_alpha[:2]): - return None - if int(array_alpha[2]) != 0x01: - return None - - payload_len = int(array_alpha[3]) - return array_alpha[4:4 + payload_len] - - -def _query_device(device): - ver_data = _xap_transaction(device, 0x00, 0x00) - if not ver_data: - return {'xap': 'UNKNOWN', 'secure': 'UNKNOWN'} - - # to u32 to BCD string - a = (ver_data[3] << 24) + (ver_data[2] << 16) + (ver_data[1] << 8) + (ver_data[0]) - ver = f'{a>>24}.{a>>16 & 0xFF}.{a & 0xFFFF}' - - secure = int.from_bytes(_xap_transaction(device, 0x00, 0x03), 'little') - secure = 'unlocked' if secure == 2 else 'LOCKED' - - return {'xap': ver, 'secure': secure} - - -def _query_device_id(device): - return _xap_transaction(device, 0x01, 0x08) - - -def _query_device_info_len(device): - len_data = _xap_transaction(device, 0x01, 0x05) - if not len_data: - return 0 - - # to u32 - return (len_data[3] << 24) + (len_data[2] << 16) + (len_data[1] << 8) + (len_data[0]) - - -def _query_device_info_chunk(device, offset): - return _xap_transaction(device, 0x01, 0x06, offset) - - -def _query_device_info(device): - datalen = _query_device_info_len(device) - if not datalen: - return {} - - data = [] - offset = 0 - while offset < datalen: - data += _query_device_info_chunk(device, offset) - offset += 32 - str_data = gzip.decompress(bytearray(data[:datalen])) - return json.loads(str_data) - - def _list_devices(): """Dump out available devices """ cli.log.info('Available devices:') - devices = _search() + devices = XAPClient.list() for dev in devices: - device = hid.Device(path=dev['path']) + device = XAPClient().connect(dev) - data = _query_device(device) - cli.log.info(" %04x:%04x %s %s [API:%s] %s", dev['vendor_id'], dev['product_id'], dev['manufacturer_string'], dev['product_string'], data['xap'], data['secure']) + 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"? - data = _query_device_info(device) - data["_id"] = _query_device_id(device) print_dotted_output(data) -# def xap_dummy(device): -# # get layer count -# layers = _xap_transaction(device, 0x04, 0x02) -# layers = int.from_bytes(layers, "little") -# print(f'layers:{layers}') - -# # get keycode [layer:0, row:0, col:0] -# # keycode = _xap_transaction(device, 0x04, 0x03, b"\x00\x00\x00") - -# # get encoder [layer:0, index:0, clockwise:0] -# keycode = _xap_transaction(device, 0x04, 0x04, b"\x00\x00\x00") - -# keycode = int.from_bytes(keycode, "little") -# print(f'keycode:{KEYCODE_MAP.get(keycode, "unknown")}[{keycode}]') - -# # set encoder [layer:0, index:0, clockwise:0, keycode:KC_A] -# _xap_transaction(device, 0x05, 0x04, b"\x00\x00\x00\x04\00") - - -def xap_broadcast_listen(device): - try: - cli.log.info("Listening for XAP broadcasts...") - while 1: - array_alpha = device.read(64, 100) - if str(b"\xFF\xFF") == str(array_alpha[:2]): - if array_alpha[2] == 1: - cli.log.info(" Broadcast: Secure[%02x]", array_alpha[4]) - else: - cli.log.info(" Broadcast: type[%02x] data:[%02x]", array_alpha[2], array_alpha[4]) - except KeyboardInterrupt: - cli.log.info("Stopping...") - - -def xap_unlock(device): - _xap_transaction(device, 0x00, 0x04) - - class XAPShell(cmd.Cmd): intro = 'Welcome to the XAP shell. Type help or ? to list commands.\n' prompt = 'Ψ> ' @@ -208,24 +242,33 @@ def __init__(self, device): cmd.Cmd.__init__(self) self.device = device # cache keycodes for this device - self.keycodes = get_xap_keycodes(_query_device(device)['xap']) + self.keycodes = get_xap_keycodes(device.version()['xap']) def do_about(self, arg): """Prints out the current version of QMK with a build date """ - data = _query_device(self.device) - print(data) + # TODO: request stuff? + print(self.device.info()['xap']) def do_unlock(self, arg): """Initiate secure unlock """ - xap_unlock(self.device) + self.device.unlock() print("Done") def do_listen(self, arg): """Log out XAP broadcast messages """ - xap_broadcast_listen(self.device) + try: + cli.log.info("Listening for XAP broadcasts...") + while 1: + array_alpha = self.device.listen() + if array_alpha[2] == 1: + cli.log.info(" Broadcast: Secure[%02x]", array_alpha[4]) + else: + cli.log.info(" Broadcast: type[%02x] data:[%02x]", array_alpha[2], array_alpha[4]) + except KeyboardInterrupt: + cli.log.info("Stopping...") def do_keycode(self, arg): """Prints out the keycode value of a certain layer, row, and column @@ -235,7 +278,7 @@ def do_keycode(self, arg): cli.log.error("Invalid args") return - keycode = _xap_transaction(self.device, 0x04, 0x03, data) + keycode = self.device.transaction(0x04, 0x03, data) keycode = int.from_bytes(keycode, "little") print(f'keycode:{self.keycodes.get(keycode, "unknown")}[{keycode}]') @@ -247,14 +290,14 @@ def do_keymap(self, arg): cli.log.error("Invalid args") return - info = _query_device_info(self.device) + 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 = _xap_transaction(self.device, 0x04, 0x03, q) + keycode = self.device.transaction(0x04, 0x03, q) keycode = int.from_bytes(keycode, "little") print(f'| {self.keycodes.get(keycode, "unknown").ljust(7)} ', end='', flush=True) print('|') @@ -267,7 +310,7 @@ def do_layer(self, arg): cli.log.error("Invalid args") return - info = _query_device_info(self.device) + info = self.device.info() # Assumptions on selected layout rather than prompt first_layout = next(iter(info['layouts'])) @@ -276,7 +319,7 @@ def do_layer(self, arg): keycodes = [] for item in layout: q = data + bytes(item['matrix']) - keycode = _xap_transaction(self.device, 0x04, 0x03, q) + keycode = self.device.transaction(0x04, 0x03, q) keycode = int.from_bytes(keycode, "little") keycodes.append(self.keycodes.get(keycode, "???")) @@ -311,22 +354,18 @@ def loop(self): def xap(cli): """Acquire debugging information from XAP devices """ - # Lazy load to avoid issues - global hid - import hid - if cli.args.list: return _list_devices() # Connect to first available device - devices = _search() + devices = XAPClient.list() if not devices: cli.log.error("No devices found!") return False dev = devices[0] - device = hid.Device(path=dev['path']) - cli.log.info("Connected to:%04x:%04x %s %s", dev['vendor_id'], dev['product_id'], dev['manufacturer_string'], dev['product_string']) + 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: From 78f28900e000a6b50c249148d8f0c6896aeafab3 Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 22 Jun 2022 23:03:37 +0100 Subject: [PATCH 100/203] Update EECONFIG version to ensure XAP init --- quantum/eeconfig.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quantum/eeconfig.h b/quantum/eeconfig.h index 565a0dbe5b94..44f42b60e378 100644 --- a/quantum/eeconfig.h +++ b/quantum/eeconfig.h @@ -21,7 +21,7 @@ along with this program. If not, see . #include #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 From 943ee73b4084d485300ffe0f9924d7b616f846ac Mon Sep 17 00:00:00 2001 From: zvecr Date: Fri, 24 Jun 2022 00:04:08 +0100 Subject: [PATCH 101/203] Reserve user/keyboard broadcast messages --- data/templates/xap/docs/route_request.md.j2 | 4 +- data/templates/xap/docs/route_response.md.j2 | 4 +- data/templates/xap/docs/routes.md.j2 | 2 +- data/xap/xap_0.1.0.hjson | 21 +++++++- docs/xap_0.0.1.md | 2 +- docs/xap_0.1.0.md | 32 +++++++------ docs/xap_0.2.0.md | 50 +++++++++++--------- 7 files changed, 70 insertions(+), 45 deletions(-) diff --git a/data/templates/xap/docs/route_request.md.j2 b/data/templates/xap/docs/route_request.md.j2 index e0148c9d15cc..fd8b81ee1636 100644 --- a/data/templates/xap/docs/route_request.md.j2 +++ b/data/templates/xap/docs/route_request.md.j2 @@ -1,8 +1,8 @@ {%- if subroute.request_type == 'struct' -%} -
__Request:__ +__Request:__ {%- for member in subroute.request_struct_members -%}
{{ " "|safe*4 }}* {{ member.name }}: `{{ member.type }}` {%- endfor -%} {%- elif subroute.request_type -%} -
__Request:__ `{{ subroute.request_type }}` +__Request:__ `{{ subroute.request_type }}` {%- endif -%} \ No newline at end of file diff --git a/data/templates/xap/docs/route_response.md.j2 b/data/templates/xap/docs/route_response.md.j2 index a36099937ad8..58af87d46757 100644 --- a/data/templates/xap/docs/route_response.md.j2 +++ b/data/templates/xap/docs/route_response.md.j2 @@ -1,8 +1,8 @@ {%- if subroute.return_type == 'struct' -%} -
__Response:__ +__Response:__ {%- for member in subroute.return_struct_members -%}
{{ " "|safe*4 }}* {{ member.name }}: `{{ member.type }}` {%- endfor -%} {%- elif subroute.return_type -%} -
__Response:__ `{{ subroute.return_type }}` +__Response:__ `{{ subroute.return_type }}` {%- endif -%} \ No newline at end of file diff --git a/data/templates/xap/docs/routes.md.j2 b/data/templates/xap/docs/routes.md.j2 index 1c1e61bd519c..791eaac5643f 100644 --- a/data/templates/xap/docs/routes.md.j2 +++ b/data/templates/xap/docs/routes.md.j2 @@ -6,7 +6,7 @@ | 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' -%}{%- include 'route_response.md.j2' -%} | {{ subroute.description.replace('\n', '
') }}| +| {{ subroute.name }} | `{{ id }} {{ subid }}` | {% if 'secure' == subroute.permissions %}__Secure__{% endif %} | {%- include 'route_request.md.j2' -%}{%- if subroute.return_type and subroute.request_type -%}

{% endif %}{%- include 'route_response.md.j2' -%} | {{ subroute.description.replace('\n', '
') }}| {%- endfor %} {% endif %} {%- endfor %} diff --git a/data/xap/xap_0.1.0.hjson b/data/xap/xap_0.1.0.hjson index 8965f2b51363..71023f9ac4d6 100755 --- a/data/xap/xap_0.1.0.hjson +++ b/data/xap/xap_0.1.0.hjson @@ -142,6 +142,23 @@ ''' 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. + ''' + } } } @@ -332,7 +349,7 @@ } 0x08: { type: command - name: Unique Identifier + name: Hardware Identifier define: HARDWARE_ID description: Retrieves a unique identifier for the board. return_type: u32[4] @@ -347,7 +364,7 @@ define: KB description: ''' - This subsystem is always present, and reserved for user-specific functionality. No routes are defined by XAP. + This subsystem is always present, and reserved for vendor-specific functionality. No routes are defined by XAP. ''' routes: { } diff --git a/docs/xap_0.0.1.md b/docs/xap_0.0.1.md index fe85fddb48e1..004d84cbb0f1 100644 --- a/docs/xap_0.0.1.md +++ b/docs/xap_0.0.1.md @@ -77,6 +77,6 @@ This subsystem is always present, and provides the ability to query information | Name | Route | Tags | Payloads | Description | | -- | -- | -- | -- | -- | -| Version Query | `0x00 0x00` | |
__Response:__ `u32`| 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}.| +| Version Query | `0x00 0x00` | |__Response:__ `u32`| 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}.| diff --git a/docs/xap_0.1.0.md b/docs/xap_0.1.0.md index 8c54aff882c6..5eb294729fdd 100644 --- a/docs/xap_0.1.0.md +++ b/docs/xap_0.1.0.md @@ -90,10 +90,10 @@ This subsystem is always present, and provides the ability to query information | Name | Route | Tags | Payloads | Description | | -- | -- | -- | -- | -- | -| Version Query | `0x00 0x00` | |
__Response:__ `u32`| 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}.| -| 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

* 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| +| Version Query | `0x00 0x00` | |__Response:__ `u32`| 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}.| +| 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

* 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| | Secure Unlock | `0x00 0x04` | || Initiate secure route unlock sequence| | Secure Lock | `0x00 0x05` | || Disable secure routes| @@ -103,18 +103,18 @@ This subsystem is always present, and provides the ability to address QMK-specif | Name | Route | Tags | Payloads | Description | | -- | -- | -- | -- | -- | -| Version Query | `0x01 0x00` | |
__Response:__ `u32`| 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}.| -| 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:__
    * Vendor ID: `u16`
    * Product ID: `u16`
    * Product Version: `u16`
    * 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`
__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

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| -| Unique Identifier | `0x01 0x08` | |
__Response:__ `u32[4]`| Retrieves a unique identifier for the board.| +| Version Query | `0x01 0x00` | |__Response:__ `u32`| 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}.| +| 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:__
    * Vendor ID: `u16`
    * Product ID: `u16`
    * Product Version: `u16`
    * 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`

__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

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| +| Hardware Identifier | `0x01 0x08` | |__Response:__ `u32[4]`| Retrieves a unique identifier for the board.| ### Keyboard - `0x02` -This subsystem is always present, and reserved for user-specific functionality. No routes are defined by XAP. +This subsystem is always present, and reserved for vendor-specific functionality. No routes are defined by XAP. ### User - `0x03` @@ -147,4 +147,8 @@ Secure status has changed. Payloads include a `u8` matching a 'Secure Status' re | --- | --- | --- | --- | --- | | **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. diff --git a/docs/xap_0.2.0.md b/docs/xap_0.2.0.md index 30f51dd863c2..bddc427f1ace 100644 --- a/docs/xap_0.2.0.md +++ b/docs/xap_0.2.0.md @@ -90,10 +90,10 @@ This subsystem is always present, and provides the ability to query information | Name | Route | Tags | Payloads | Description | | -- | -- | -- | -- | -- | -| Version Query | `0x00 0x00` | |
__Response:__ `u32`| 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}.| -| 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

* 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| +| Version Query | `0x00 0x00` | |__Response:__ `u32`| 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}.| +| 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

* 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| | Secure Unlock | `0x00 0x04` | || Initiate secure route unlock sequence| | Secure Lock | `0x00 0x05` | || Disable secure routes| @@ -103,18 +103,18 @@ This subsystem is always present, and provides the ability to address QMK-specif | Name | Route | Tags | Payloads | Description | | -- | -- | -- | -- | -- | -| Version Query | `0x01 0x00` | |
__Response:__ `u32`| 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}.| -| 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:__
    * Vendor ID: `u16`
    * Product ID: `u16`
    * Product Version: `u16`
    * 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`
__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

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| -| Unique Identifier | `0x01 0x08` | |
__Response:__ `u32[4]`| Retrieves a unique identifier for the board.| +| Version Query | `0x01 0x00` | |__Response:__ `u32`| 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}.| +| 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:__
    * Vendor ID: `u16`
    * Product ID: `u16`
    * Product Version: `u16`
    * 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`

__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

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| +| Hardware Identifier | `0x01 0x08` | |__Response:__ `u32[4]`| Retrieves a unique identifier for the board.| ### Keyboard - `0x02` -This subsystem is always present, and reserved for user-specific functionality. No routes are defined by XAP. +This subsystem is always present, and reserved for vendor-specific functionality. No routes are defined by XAP. ### User - `0x03` @@ -127,10 +127,10 @@ 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:__
    * Layer: `u8`
    * Row: `u8`
    * Column: `u8`
__Response:__ `u16`| Query the Keycode at the requested location.| -| Get Encoder Keycode | `0x04 0x04` | |
__Request:__
    * Layer: `u8`
    * Encoder: `u8`
    * Clockwise: `u8`
__Response:__ `u16`| Query the Keycode at the requested location.| +| 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:__
    * Layer: `u8`
    * Row: `u8`
    * Column: `u8`

__Response:__ `u16`| Query the Keycode at the requested location.| +| Get Encoder Keycode | `0x04 0x04` | |__Request:__
    * Layer: `u8`
    * Encoder: `u8`
    * Clockwise: `u8`

__Response:__ `u16`| Query the Keycode at the requested location.| ### Remapping - `0x05` This subsystem allows for live reassignment of keycodes without rebuilding the firmware. @@ -138,10 +138,10 @@ This subsystem allows for live reassignment of keycodes without rebuilding the f | 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:__
    * Layer: `u8`
    * Row: `u8`
    * Column: `u8`
    * Keycode: `u16`| Modify the Keycode at the requested location.| -| Set Encoder Keycode | `0x05 0x04` | __Secure__ |
__Request:__
    * Layer: `u8`
    * Encoder: `u8`
    * Clockwise: `u8`
    * Keycode: `u16`| Modify the Keycode at the requested location.| +| 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:__
    * Layer: `u8`
    * Row: `u8`
    * Column: `u8`
    * Keycode: `u16`| Modify the Keycode at the requested location.| +| Set Encoder Keycode | `0x05 0x04` | __Secure__ |__Request:__
    * Layer: `u8`
    * Encoder: `u8`
    * Clockwise: `u8`
    * Keycode: `u16`| Modify the Keycode at the requested location.| ### Lighting - `0x06` This subsystem allows for control over the lighting subsystem. @@ -149,7 +149,7 @@ 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.| +| Capabilities Query | `0x06 0x01` | |__Response:__ `u32`| Lighting subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.| ## Broadcast messages @@ -177,4 +177,8 @@ Secure status has changed. Payloads include a `u8` matching a 'Secure Status' re | --- | --- | --- | --- | --- | | **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. From 2592402627e58a121347842d4f26b39e78adbadf Mon Sep 17 00:00:00 2001 From: zvecr Date: Mon, 27 Jun 2022 20:22:10 +0100 Subject: [PATCH 102/203] Use keymap folder hash to reset dynamic keymap eeprom --- builddefs/build_keyboard.mk | 7 ++++- lib/python/qmk/cli/__init__.py | 1 + lib/python/qmk/cli/generate/keymap_hash.py | 36 ++++++++++++++++++++++ quantum/eeconfig.c | 20 +++++++++--- quantum/eeconfig.h | 5 ++- 5 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 lib/python/qmk/cli/generate/keymap_hash.py diff --git a/builddefs/build_keyboard.mk b/builddefs/build_keyboard.mk index 2b9ec48e7045..ee9ec3a3abd2 100644 --- a/builddefs/build_keyboard.mk +++ b/builddefs/build_keyboard.mk @@ -344,7 +344,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 diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py index 8bfba924cdd7..fe9f1c78b385 100644 --- a/lib/python/qmk/cli/__init__.py +++ b/lib/python/qmk/cli/__init__.py @@ -55,6 +55,7 @@ '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', diff --git a/lib/python/qmk/cli/generate/keymap_hash.py b/lib/python/qmk/cli/generate/keymap_hash.py new file mode 100644 index 000000000000..4a0bff83a46d --- /dev/null +++ b/lib/python/qmk/cli/generate/keymap_hash.py @@ -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) diff --git a/quantum/eeconfig.c b/quantum/eeconfig.c index c829e9d263ed..0698b1bd14a5 100644 --- a/quantum/eeconfig.c +++ b/quantum/eeconfig.c @@ -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,10 +84,9 @@ void eeconfig_init_quantum(void) { // properly re-initialized. via_eeprom_set_valid(false); eeconfig_init_via(); -#elif defined(XAP_ENABLE) - // TODO: define XAP reset behaviour - void dynamic_keymap_reset(void); +#elif defined(DYNAMIC_KEYMAP_ENABLE) dynamic_keymap_reset(); + eeprom_update_dword(EECONFIG_KEYMAP_HASH, KEYMAP_HASH); #endif eeconfig_init_kb(); @@ -123,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; } @@ -137,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; } diff --git a/quantum/eeconfig.h b/quantum/eeconfig.h index 44f42b60e378..c3709c1ee228 100644 --- a/quantum/eeconfig.h +++ b/quantum/eeconfig.h @@ -53,8 +53,11 @@ along with this program. If not, see . // 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) From c2b2f9feac83ca1de6e55ea9292a528aa87606f8 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Sat, 2 Jul 2022 22:33:39 +1000 Subject: [PATCH 103/203] Fix line endings. --- keyboards/ms_sculpt/sculpt_iso.h | 50 ++++++++++++++++---------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/keyboards/ms_sculpt/sculpt_iso.h b/keyboards/ms_sculpt/sculpt_iso.h index 9ffcbae6c31c..056f7d5b5de5 100644 --- a/keyboards/ms_sculpt/sculpt_iso.h +++ b/keyboards/ms_sculpt/sculpt_iso.h @@ -1,25 +1,25 @@ -// Copyright 2022 Jean Bernard (@jn-bernard) -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "quantum.h" - -#define LAYOUT_iso( \ - k4D, k7C, k7B, k1B, k4B, k1A, k1F, k79, k77, k75, k78, k73, k13, k71, k31, k01, k23, \ - k7D, k0D, k0C, k2D, k1D, k7A, k7F, k09, k07, k05, k04, k15, k74, k08, k03, k21, \ - k0A, k0B, k1C, k2B, k2A, k2F, k19, k29, k27, k25, k24, k14, k17, k51, \ - k2C, k3D, k4C, k3B, k3A, k4A, k49, k39, k37, k35, k34, k45, k65, k33, k18, k11, \ - k5E, k6C, k5D, k5C, k5B, k5A, k6A, k69, k59, k57, k55, k44, k52, k63, k53, \ - k6H, k3F, k4G, k6B, k68, k46, k43, k60, k48, k64, k61 \ -) \ -{ \ - {KC_NO, k01, KC_NO, k03, k04, k05, KC_NO, k07, k08, k09, k0A, k0B, k0C, k0D, KC_NO, KC_NO, KC_NO, KC_NO}, \ - {KC_NO, k11, KC_NO, k13, k14, k15, KC_NO, k17, k18, k19, k1A, k1B, k1C, k1D, KC_NO, k1F, KC_NO, KC_NO}, \ - {KC_NO, k21, KC_NO, k23, k24, k25, KC_NO, k27, KC_NO, k29, k2A, k2B, k2C, k2D, KC_NO, k2F, KC_NO, KC_NO}, \ - {KC_NO, k31, KC_NO, k33, k34, k35, KC_NO, k37, KC_NO, k39, k3A, k3B, k6C, k3D, KC_NO, k3F, KC_NO, KC_NO}, \ - {KC_NO, KC_NO, KC_NO, k43, k44, k45, k46, KC_NO, k48, k49, k4A, k4B, k4C, k4D, KC_NO, KC_NO, k4G, KC_NO}, \ - {KC_NO, k51, k52, k53, k65, k55, KC_NO, k57, KC_NO, k59, k5A, k5B, k5C, k5D, k5E, KC_NO, KC_NO, KC_NO}, \ - {k60, k61, KC_NO, k63, k64, KC_NO, KC_NO, KC_NO, k68, k69, k6A, k6B, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, k6H}, \ - {KC_NO, k71, KC_NO, k73, k74, k75, KC_NO, k77, k78, k79, k7A, k7B, k7C, k7D, KC_NO, k7F, KC_NO, KC_NO} \ -} +// Copyright 2022 Jean Bernard (@jn-bernard) +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "quantum.h" + +#define LAYOUT_iso( \ + k4D, k7C, k7B, k1B, k4B, k1A, k1F, k79, k77, k75, k78, k73, k13, k71, k31, k01, k23, \ + k7D, k0D, k0C, k2D, k1D, k7A, k7F, k09, k07, k05, k04, k15, k74, k08, k03, k21, \ + k0A, k0B, k1C, k2B, k2A, k2F, k19, k29, k27, k25, k24, k14, k17, k51, \ + k2C, k3D, k4C, k3B, k3A, k4A, k49, k39, k37, k35, k34, k45, k65, k33, k18, k11, \ + k5E, k6C, k5D, k5C, k5B, k5A, k6A, k69, k59, k57, k55, k44, k52, k63, k53, \ + k6H, k3F, k4G, k6B, k68, k46, k43, k60, k48, k64, k61 \ +) \ +{ \ + {KC_NO, k01, KC_NO, k03, k04, k05, KC_NO, k07, k08, k09, k0A, k0B, k0C, k0D, KC_NO, KC_NO, KC_NO, KC_NO}, \ + {KC_NO, k11, KC_NO, k13, k14, k15, KC_NO, k17, k18, k19, k1A, k1B, k1C, k1D, KC_NO, k1F, KC_NO, KC_NO}, \ + {KC_NO, k21, KC_NO, k23, k24, k25, KC_NO, k27, KC_NO, k29, k2A, k2B, k2C, k2D, KC_NO, k2F, KC_NO, KC_NO}, \ + {KC_NO, k31, KC_NO, k33, k34, k35, KC_NO, k37, KC_NO, k39, k3A, k3B, k6C, k3D, KC_NO, k3F, KC_NO, KC_NO}, \ + {KC_NO, KC_NO, KC_NO, k43, k44, k45, k46, KC_NO, k48, k49, k4A, k4B, k4C, k4D, KC_NO, KC_NO, k4G, KC_NO}, \ + {KC_NO, k51, k52, k53, k65, k55, KC_NO, k57, KC_NO, k59, k5A, k5B, k5C, k5D, k5E, KC_NO, KC_NO, KC_NO}, \ + {k60, k61, KC_NO, k63, k64, KC_NO, KC_NO, KC_NO, k68, k69, k6A, k6B, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, k6H}, \ + {KC_NO, k71, KC_NO, k73, k74, k75, KC_NO, k77, k78, k79, k7A, k7B, k7C, k7D, KC_NO, k7F, KC_NO, KC_NO} \ +} From 8b133897dc7ebfade1f902ad5f844e8c150a2369 Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 6 Jul 2022 17:10:04 +0100 Subject: [PATCH 104/203] Split client out --- lib/python/qmk/cli/xap/xap.py | 234 ++++----------------------- lib/python/qmk/cli/xap/xap_client.py | 186 +++++++++++++++++++++ 2 files changed, 216 insertions(+), 204 deletions(-) create mode 100644 lib/python/qmk/cli/xap/xap_client.py diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index 2784a631bbfb..8375fa50331d 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -1,195 +1,15 @@ """Interactions with compatible XAP devices """ import cmd -import json -import random -import gzip -import threading -import functools -from enum import IntFlag -from platform import platform from milc import cli from qmk.keyboard import render_layout from qmk.xap.common import get_xap_keycodes -KEYCODE_MAP = get_xap_keycodes('latest') - - -def _u32toBCD(val): # noqa: N802 - """Create BCD string - """ - return f'{val>>24}.{val>>16 & 0xFF}.{val & 0xFFFF}' - - -class XAPFlags(IntFlag): - SUCCESS = 0x01 - - -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 = str(array_alpha[:2]) - event = self.responses.get(token) - if event: - event._ret = array_alpha - event.set() - - def _query_device_info(self): - datalen = int.from_bytes(self.transaction(0x01, 0x05) or bytes(0), "little") - if not datalen: - return {} - - data = [] - offset = 0 - while offset < datalen: - chunk = self.transaction(0x01, 0x06, 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 = b"\xFF\xFF" - event = threading.Event() - self.responses[str(token)] = event - - event.wait() - return event._ret - - def transaction(self, sub, route, *args): - """Request/Receive - """ - # token cannot start with zero or be FFFF - token = random.randrange(0x0100, 0xFFFE).to_bytes(2, byteorder='big') - - # send with padding - # TODO: this code is total garbage - args_data = [] - args_len = 2 - if len(args) == 1: - if isinstance(args[0], (bytes, bytearray)): - args_len += len(args[0]) - args_data = args[0] - else: - args_len += 2 - args_data = args[0].to_bytes(2, byteorder='little') - - padding_len = 64 - 3 - args_len - padding = b"\x00" * padding_len - if args_data: - padding = args_data + padding - buffer = token + args_len.to_bytes(1, byteorder='little') + sub.to_bytes(1, byteorder='little') + route.to_bytes(1, byteorder='little') + padding - - # prepend 0 on windows because reasons... - if 'windows' in platform().lower(): - buffer = b"\x00" + buffer - - event = threading.Event() - self.responses[str(token)] = event - - self.dev.write(buffer) - event.wait(timeout=1) - self.responses.pop(str(token), None) - if not hasattr(event, '_ret'): - return None - - array_alpha = event._ret - if int(array_alpha[2]) != XAPFlags.SUCCESS: - return None - - payload_len = int(array_alpha[3]) - return array_alpha[4:4 + payload_len] - - @functools.cache - def version(self): - ver = int.from_bytes(self.transaction(0x00, 0x00) or bytes(0), 'little') - return {'xap': _u32toBCD(ver)} - - @functools.cache - def info(self): - data = self._query_device_info() - data['_id'] = self.transaction(0x01, 0x08) - data['xap'] = self.version()['xap'] - return data - - def unlock(self): - self.transaction(0x00, 0x04) - - -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) - +from .xap_client import XAPClient, XAPEventType, XAPSecureStatus -# def _query_device_secure(device): -# secure = int.from_bytes(_xap_transaction(device, 0x00, 0x03), 'little') -# secure = 'unlocked' if secure == 2 else 'LOCKED' -# return {'secure': secure} -# -# def xap_dummy(device): -# # get layer count -# layers = _xap_transaction(device, 0x04, 0x02) -# layers = int.from_bytes(layers, "little") -# print(f'layers:{layers}') - -# # get keycode [layer:0, row:0, col:0] -# # keycode = _xap_transaction(device, 0x04, 0x03, b"\x00\x00\x00") - -# # get encoder [layer:0, index:0, clockwise:0] -# keycode = _xap_transaction(device, 0x04, 0x04, b"\x00\x00\x00") - -# keycode = int.from_bytes(keycode, "little") -# print(f'keycode:{KEYCODE_MAP.get(keycode, "unknown")}[{keycode}]') - -# # set encoder [layer:0, index:0, clockwise:0, keycode:KC_A] -# _xap_transaction(device, 0x05, 0x04, b"\x00\x00\x00\x04\00") +KEYCODE_MAP = get_xap_keycodes('latest') def print_dotted_output(kb_info_json, prefix=''): @@ -227,10 +47,10 @@ def _list_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']) + 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"? + # TODO: better formatting like 'lsusb -v'? print_dotted_output(data) @@ -254,32 +74,38 @@ def do_unlock(self, arg): """Initiate secure unlock """ self.device.unlock() - print("Done") + print('Unlock Requested...') def do_listen(self, arg): """Log out XAP broadcast messages """ try: - cli.log.info("Listening for XAP broadcasts...") + cli.log.info('Listening for XAP broadcasts...') while 1: - array_alpha = self.device.listen() - if array_alpha[2] == 1: - cli.log.info(" Broadcast: Secure[%02x]", array_alpha[4]) + (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:[%02x]", array_alpha[2], array_alpha[4]) + data_str = ' '.join(['{:02X}'.format(b) for b in data]) + + cli.log.info(' Broadcast: type[%02x] data:[%s]', event, data_str) + except KeyboardInterrupt: - cli.log.info("Stopping...") + 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") + cli.log.error('Invalid args') return - keycode = self.device.transaction(0x04, 0x03, data) - keycode = int.from_bytes(keycode, "little") + 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): @@ -287,7 +113,7 @@ def do_keymap(self, arg): """ data = bytes(map(int, arg.split())) if len(data) != 1: - cli.log.error("Invalid args") + cli.log.error('Invalid args') return info = self.device.info() @@ -297,8 +123,8 @@ def do_keymap(self, arg): 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(0x04, 0x03, q) - keycode = int.from_bytes(keycode, "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('|') @@ -307,7 +133,7 @@ def do_layer(self, arg): """ data = bytes(map(int, arg.split())) if len(data) != 1: - cli.log.error("Invalid args") + cli.log.error('Invalid args') return info = self.device.info() @@ -319,9 +145,9 @@ def do_layer(self, arg): keycodes = [] for item in layout: q = data + bytes(item['matrix']) - keycode = self.device.transaction(0x04, 0x03, q) - keycode = int.from_bytes(keycode, "little") - keycodes.append(self.keycodes.get(keycode, "???")) + 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)) @@ -360,11 +186,11 @@ def xap(cli): # Connect to first available device devices = XAPClient.list() if not devices: - cli.log.error("No devices found!") + 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']) + 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? @@ -372,4 +198,4 @@ def xap(cli): XAPShell(device).loop() return True - XAPShell(device).onecmd(" ".join(cli.args.action)) + XAPShell(device).onecmd(' '.join(cli.args.action)) diff --git a/lib/python/qmk/cli/xap/xap_client.py b/lib/python/qmk/cli/xap/xap_client.py new file mode 100644 index 000000000000..c7803b687fa7 --- /dev/null +++ b/lib/python/qmk/cli/xap/xap_client.py @@ -0,0 +1,186 @@ +"""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('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 unlock(self): + self.transaction(b'\x00\x04') + + +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) From 6ec0ff387b0a4ca92941f4ad4cf75fb00ddda329 Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 7 Jul 2022 00:57:59 +0100 Subject: [PATCH 105/203] Update reserved tokens --- data/xap/xap_0.0.1.hjson | 7 +++++-- data/xap/xap_0.1.0.hjson | 4 ++-- docs/xap_0.0.1.md | 7 +++++-- docs/xap_0.1.0.md | 11 +++++++---- docs/xap_0.2.0.md | 11 +++++++---- lib/python/qmk/cli/xap/xap_client.py | 4 ++-- 6 files changed, 28 insertions(+), 16 deletions(-) diff --git a/data/xap/xap_0.0.1.hjson b/data/xap/xap_0.0.1.hjson index 2b6def0ff135..aa34d7828d38 100755 --- a/data/xap/xap_0.0.1.hjson +++ b/data/xap/xap_0.0.1.hjson @@ -48,7 +48,10 @@ 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, allowing 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. + 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. ''' @@ -146,7 +149,7 @@ token: { name: Token - description: A `u16` associated with a specific request as well as its corresponding response. + 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 } diff --git a/data/xap/xap_0.1.0.hjson b/data/xap/xap_0.1.0.hjson index 71023f9ac4d6..75beb98e8960 100755 --- a/data/xap/xap_0.1.0.hjson +++ b/data/xap/xap_0.1.0.hjson @@ -13,8 +13,8 @@ reserved_tokens: ''' - Two token values are reserved: `0x0000` and `0xFFFF`: - * `0x0000`: A message sent by a host application may use this token if no response is to be sent -- a "fire and forget" message. + 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. diff --git a/docs/xap_0.0.1.md b/docs/xap_0.0.1.md index 004d84cbb0f1..410a2d9d32d7 100644 --- a/docs/xap_0.0.1.md +++ b/docs/xap_0.0.1.md @@ -28,13 +28,16 @@ This list defines the terms used across the entire set of XAP protocol documenta | _Request Header_ | Packet format for inbound data. Takes the format:
`token` - token
`u8` - length | | _Response Flags_ | An `u8` containing the status of the request. | | _Response Header_ | Packet format for outbound data. Takes the format:
`token` - token
`response_flags` - flags
`u8` - length | -| _Token_ | A `u16` associated with a specific request as well as its corresponding response. | +| _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, allowing 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. +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. diff --git a/docs/xap_0.1.0.md b/docs/xap_0.1.0.md index 5eb294729fdd..bdfd62df343c 100644 --- a/docs/xap_0.1.0.md +++ b/docs/xap_0.1.0.md @@ -34,18 +34,21 @@ This list defines the terms used across the entire set of XAP protocol documenta | _Request Header_ | Packet format for inbound data. Takes the format:
`token` - token
`u8` - length | | _Response Flags_ | An `u8` containing the status of the request. | | _Response Header_ | Packet format for outbound data. Takes the format:
`token` - token
`response_flags` - flags
`u8` - length | -| _Token_ | A `u16` associated with a specific request as well as its corresponding response. | +| _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, allowing 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. +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: `0x0000` and `0xFFFF`: -* `0x0000`: A message sent by a host application may use this token if no response is to be sent -- a "fire and forget" message. +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. diff --git a/docs/xap_0.2.0.md b/docs/xap_0.2.0.md index bddc427f1ace..1b3bedc7fcc2 100644 --- a/docs/xap_0.2.0.md +++ b/docs/xap_0.2.0.md @@ -34,18 +34,21 @@ This list defines the terms used across the entire set of XAP protocol documenta | _Request Header_ | Packet format for inbound data. Takes the format:
`token` - token
`u8` - length | | _Response Flags_ | An `u8` containing the status of the request. | | _Response Header_ | Packet format for outbound data. Takes the format:
`token` - token
`response_flags` - flags
`u8` - length | -| _Token_ | A `u16` associated with a specific request as well as its corresponding response. | +| _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, allowing 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. +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: `0x0000` and `0xFFFF`: -* `0x0000`: A message sent by a host application may use this token if no response is to be sent -- a "fire and forget" message. +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. diff --git a/lib/python/qmk/cli/xap/xap_client.py b/lib/python/qmk/cli/xap/xap_client.py index c7803b687fa7..a7e47645e6f3 100644 --- a/lib/python/qmk/cli/xap/xap_client.py +++ b/lib/python/qmk/cli/xap/xap_client.py @@ -18,9 +18,9 @@ def _gen_token(): - """Generate XAP token - cannot start with 00xx or be FFFF + """Generate XAP token - cannot start with 00xx or 'reserved' (FFFE|FFFF) """ - token = random.randrange(0x0100, 0xFFFE) + token = random.randrange(0x0100, 0xFFFD) # swap endianness return unpack('H', token))[0] From ced7094ddd9bfa45c706049d689a6bcad3a56584 Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 7 Jul 2022 01:57:41 +0100 Subject: [PATCH 106/203] Add xap cli functions to test secure --- builddefs/common_features.mk | 1 + lib/python/qmk/cli/xap/xap.py | 22 +++++++++++++++++++--- lib/python/qmk/cli/xap/xap_client.py | 14 ++++++++++++++ quantum/xap/xap_handlers.c | 5 ----- 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk index a21f530610f8..0e0e36cb6afc 100644 --- a/builddefs/common_features.mk +++ b/builddefs/common_features.mk @@ -876,6 +876,7 @@ ifeq ($(strip $(XAP_ENABLE)), yes) endif OPT_DEFS += -DXAP_ENABLE + OPT_DEFS += -DBOOTLOADER_JUMP_SUPPORTED DYNAMIC_KEYMAP_ENABLE := yes SECURE_ENABLE := yes EMBED_INFO_JSON := yes diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index 8375fa50331d..0ae425d93cd4 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -70,12 +70,30 @@ def do_about(self, arg): # 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 """ @@ -89,9 +107,7 @@ def do_listen(self, arg): cli.log.info(' Secure[%s]', secure_status) else: - data_str = ' '.join(['{:02X}'.format(b) for b in data]) - - cli.log.info(' Broadcast: type[%02x] data:[%s]', event, data_str) + cli.log.info(' Broadcast: type[%02x] data:[%s]', event, data.hex()) except KeyboardInterrupt: cli.log.info('Stopping...') diff --git a/lib/python/qmk/cli/xap/xap_client.py b/lib/python/qmk/cli/xap/xap_client.py index a7e47645e6f3..826a79c91f62 100644 --- a/lib/python/qmk/cli/xap/xap_client.py +++ b/lib/python/qmk/cli/xap/xap_client.py @@ -148,9 +148,23 @@ def info(self): 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 diff --git a/quantum/xap/xap_handlers.c b/quantum/xap/xap_handlers.c index 8ae9bcedf511..1d99cff875ae 100644 --- a/quantum/xap/xap_handlers.c +++ b/quantum/xap/xap_handlers.c @@ -83,11 +83,6 @@ bool xap_respond_secure_lock(xap_token_t token, const void *data, size_t length) return xap_respond_data(token, NULL, 0); } -// TODO: how to set this if "custom" is just an empty stub -#ifndef BOOTLOADER_JUMP_SUPPORTED -# define BOOTLOADER_JUMP_SUPPORTED -#endif - #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(); From f913f126b4662a299eceae37fea6829979e6b65c Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 7 Jul 2022 16:48:18 +0100 Subject: [PATCH 107/203] Add in some defaults to info.json payload --- .../qmk/xap/gen_firmware/blob_generator.py | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/lib/python/qmk/xap/gen_firmware/blob_generator.py b/lib/python/qmk/xap/gen_firmware/blob_generator.py index 13e19688dfa5..3d592426657b 100644 --- a/lib/python/qmk/xap/gen_firmware/blob_generator.py +++ b/lib/python/qmk/xap/gen_firmware/blob_generator.py @@ -2,22 +2,38 @@ """ 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 generate_blob(output_file, keyboard, keymap): - # Build the info.json file +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 km_info_json['config_h_features'] + 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(km_info_json, separators=(',', ':')) + str_data = json.dumps(info_json, separators=(',', ':')) # Compress compressed = gzip.compress(str_data.encode("utf-8"), compresslevel=9) From 9b9dc839d280bf00426469de60bd68959e8218eb Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 7 Jul 2022 19:25:53 +0100 Subject: [PATCH 108/203] Add in some defaults to info.json payload --- data/mappings/xap_defaults.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 data/mappings/xap_defaults.json diff --git a/data/mappings/xap_defaults.json b/data/mappings/xap_defaults.json new file mode 100644 index 000000000000..9bf3461213df --- /dev/null +++ b/data/mappings/xap_defaults.json @@ -0,0 +1,7 @@ +{ + "secure": { + "unlock_sequence": [ [0,0] ], + "unlock_timeout": 5000, + "idle_timeout": 60000 + } +} \ No newline at end of file From 10218c385b34b9a7355f58828e7c0eb8acca4fda Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 7 Jul 2022 22:59:12 +0100 Subject: [PATCH 109/203] Align config length/offset data types --- data/xap/xap_0.1.0.hjson | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/xap/xap_0.1.0.hjson b/data/xap/xap_0.1.0.hjson index 75beb98e8960..5eb1459d6551 100755 --- a/data/xap/xap_0.1.0.hjson +++ b/data/xap/xap_0.1.0.hjson @@ -316,7 +316,7 @@ name: Config Blob Length define: CONFIG_BLOB_LEN description: Retrieves the length of the configuration data bundled within the firmware - return_type: u32 + return_type: u16 return_constant: CONFIG_BLOB_GZ_LEN } 0x06: { From c155b16ec9ff0567a279ab24f07272f27c688d81 Mon Sep 17 00:00:00 2001 From: zvecr Date: Sat, 9 Jul 2022 00:21:34 +0100 Subject: [PATCH 110/203] Minor tidy up of cli --- lib/python/qmk/cli/xap/xap.py | 4 ++-- lib/python/qmk/cli/xap/xap_client.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index 0ae425d93cd4..be86e0a6f0bf 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -191,7 +191,7 @@ def loop(self): @cli.argument('-d', '--device', help='device to select - uses format :.') @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.argument('action', nargs='*', arg_only=True, default=['listen'], help='Shell command and any arguments to run standalone') @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 @@ -206,7 +206,7 @@ def xap(cli): 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']) + 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? diff --git a/lib/python/qmk/cli/xap/xap_client.py b/lib/python/qmk/cli/xap/xap_client.py index 826a79c91f62..c279e8811829 100644 --- a/lib/python/qmk/cli/xap/xap_client.py +++ b/lib/python/qmk/cli/xap/xap_client.py @@ -67,7 +67,7 @@ def _read_loop(self): """Background thread to signal waiting transactions """ while 1: - array_alpha = self.dev.read(64, 100) + array_alpha = self.dev.read(ResponseStruct.size, 100) if array_alpha: token = int.from_bytes(array_alpha[:2], 'little') event = self.responses.get(token) From 98fd483611afc898c36fe4f4ffe70dd48b0e9ab6 Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 13 Jul 2022 03:01:03 +0100 Subject: [PATCH 111/203] XAP Client - Ensure route exists --- lib/python/qmk/cli/xap/xap_client.py | 39 ++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/lib/python/qmk/cli/xap/xap_client.py b/lib/python/qmk/cli/xap/xap_client.py index c279e8811829..fbb18245a97d 100644 --- a/lib/python/qmk/cli/xap/xap_client.py +++ b/lib/python/qmk/cli/xap/xap_client.py @@ -52,6 +52,10 @@ class XAPEventType(IntEnum): USER = 0x03 +class XAPRouteError(Exception): + pass + + class XAPDevice: def __init__(self, dev): """Constructor opens hid device and starts dependent services @@ -96,12 +100,13 @@ def listen(self): event = threading.Event() self.responses[token] = event - event.wait() + while not hasattr(event, '_ret'): + event.wait(timeout=0.25) r = ResponsePacket._make(ResponseStruct.unpack(event._ret)) return (r.flags, r.data[:r.length]) - def transaction(self, *args): + def _transaction(self, *args): """Request/Receive """ # convert args to array of bytes @@ -136,12 +141,36 @@ def transaction(self, *args): return r.data[:r.length] - @functools.cache + @functools.lru_cache + def capability(self, route): + cap = int.from_bytes(self._transaction(route) or bytes(0), 'little') + return cap + + @functools.lru_cache + def subsystem(self): + sub = int.from_bytes(self._transaction(b'\x00\x02') or bytes(0), 'little') + return sub + + @functools.lru_cache def version(self): - ver = int.from_bytes(self.transaction(b'\x00\x00') or bytes(0), 'little') + ver = int.from_bytes(self._transaction(b'\x00\x00') or bytes(0), 'little') return {'xap': _u32toBCD(ver)} - @functools.cache + def _ensure_route(self, route): + (sub, rt) = route + cap = bytes([sub, 1]) + + if self.subsystem() & (1 << sub) == 0: + raise XAPRouteError("subsystem not available") + if self.capability(cap) & (1 << rt) == 0: + raise XAPRouteError("route not available") + + def transaction(self, route, *args): + self._ensure_route(route) + + return self._transaction(route, *args) + + @functools.lru_cache def info(self): data = self._query_device_info() data['_id'] = self.transaction(b'\x01\x08') From f4e5901726c9f8fab28c515d3fe97e0380e9717d Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 13 Jul 2022 23:49:55 +0100 Subject: [PATCH 112/203] Strip out 'permissions: ignore' --- data/schemas/xap.jsonschema | 3 +-- data/xap/xap_0.1.0.hjson | 18 ------------------ docs/xap_0.1.0.md | 6 ++---- docs/xap_0.2.0.md | 6 ++---- lib/python/qmk/cli/xap/xap_client.py | 4 +--- .../qmk/xap/gen_firmware/inline_generator.py | 1 - quantum/xap/xap.c | 6 ------ 7 files changed, 6 insertions(+), 38 deletions(-) diff --git a/data/schemas/xap.jsonschema b/data/schemas/xap.jsonschema index db0e7c4e87fc..0e3189561f06 100644 --- a/data/schemas/xap.jsonschema +++ b/data/schemas/xap.jsonschema @@ -30,8 +30,7 @@ }, "permission": { "enum": [ - "secure", - "ignore" + "secure" ] }, "struct": { diff --git a/data/xap/xap_0.1.0.hjson b/data/xap/xap_0.1.0.hjson index 5eb1459d6551..c4988a224648 100755 --- a/data/xap/xap_0.1.0.hjson +++ b/data/xap/xap_0.1.0.hjson @@ -38,22 +38,6 @@ 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. - ''' - } } } @@ -202,7 +186,6 @@ * 2 means secure routes are allowed * any other value should be interpreted as disabled ''' - permissions: ignore return_type: u8 return_execute: secure_status } @@ -217,7 +200,6 @@ type: command name: Secure Lock define: SECURE_LOCK - permissions: ignore description: Disable secure routes return_execute: secure_lock } diff --git a/docs/xap_0.1.0.md b/docs/xap_0.1.0.md index bdfd62df343c..3fa4dc00ff91 100644 --- a/docs/xap_0.1.0.md +++ b/docs/xap_0.1.0.md @@ -57,10 +57,8 @@ Response messages will always be prefixed by the originating request _token_, di | Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | | -- | -- | -- | -- | -- | -- | -- | -- | -| `UNLOCKED` | `UNLOCK_IN_PROGRESS` | `-` | `-` | `-` | `-` | `SECURE_FAILURE` | `SUCCESS` | +| `-` | `-` | `-` | `-` | `-` | `-` | `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). @@ -111,7 +109,7 @@ This subsystem is always present, and provides the ability to address QMK-specif | Board identifiers | `0x01 0x02` | |__Response:__
    * Vendor ID: `u16`
    * Product ID: `u16`
    * Product Version: `u16`
    * 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 Length | `0x01 0x05` | |__Response:__ `u16`| Retrieves the length of the configuration data bundled within the firmware| | Config Blob Chunk | `0x01 0x06` | |__Request:__ `u16`

__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

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| | Hardware Identifier | `0x01 0x08` | |__Response:__ `u32[4]`| Retrieves a unique identifier for the board.| diff --git a/docs/xap_0.2.0.md b/docs/xap_0.2.0.md index 1b3bedc7fcc2..560e141fdd25 100644 --- a/docs/xap_0.2.0.md +++ b/docs/xap_0.2.0.md @@ -57,10 +57,8 @@ Response messages will always be prefixed by the originating request _token_, di | Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | | -- | -- | -- | -- | -- | -- | -- | -- | -| `UNLOCKED` | `UNLOCK_IN_PROGRESS` | `-` | `-` | `-` | `-` | `SECURE_FAILURE` | `SUCCESS` | +| `-` | `-` | `-` | `-` | `-` | `-` | `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). @@ -111,7 +109,7 @@ This subsystem is always present, and provides the ability to address QMK-specif | Board identifiers | `0x01 0x02` | |__Response:__
    * Vendor ID: `u16`
    * Product ID: `u16`
    * Product Version: `u16`
    * 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 Length | `0x01 0x05` | |__Response:__ `u16`| Retrieves the length of the configuration data bundled within the firmware| | Config Blob Chunk | `0x01 0x06` | |__Request:__ `u16`

__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

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| | Hardware Identifier | `0x01 0x08` | |__Response:__ `u32[4]`| Retrieves a unique identifier for the board.| diff --git a/lib/python/qmk/cli/xap/xap_client.py b/lib/python/qmk/cli/xap/xap_client.py index fbb18245a97d..a12f54e35bc1 100644 --- a/lib/python/qmk/cli/xap/xap_client.py +++ b/lib/python/qmk/cli/xap/xap_client.py @@ -42,8 +42,6 @@ class XAPFlags(IntFlag): FAILURE = 0 SUCCESS = 1 << 0 SECURE_FAILURE = 1 << 1 - UNLOCK_IN_PROGRESS = 1 << 6 - UNLOCKED = 1 << 7 class XAPEventType(IntEnum): @@ -136,7 +134,7 @@ def _transaction(self, *args): return None r = ResponsePacket._make(ResponseStruct.unpack(event._ret)) - if r.flags != XAPFlags.SUCCESS: + if r.flags & XAPFlags.SUCCESS == 0: return None return r.data[:r.length] diff --git a/lib/python/qmk/xap/gen_firmware/inline_generator.py b/lib/python/qmk/xap/gen_firmware/inline_generator.py index 44170a18acdc..f8291b5e06ab 100755 --- a/lib/python/qmk/xap/gen_firmware/inline_generator.py +++ b/lib/python/qmk/xap/gen_firmware/inline_generator.py @@ -128,7 +128,6 @@ def _append_routing_table_entry_flags(lines, container, container_id, route_stac pem_map = { None: 'ROUTE_PERMISSIONS_INSECURE', 'secure': 'ROUTE_PERMISSIONS_SECURE', - 'ignore': 'ROUTE_PERMISSIONS_IGNORE', } is_secure = pem_map[container.get('permissions', None)] diff --git a/quantum/xap/xap.c b/quantum/xap/xap.c index 83bdbe13f396..741851b0251d 100644 --- a/quantum/xap/xap.c +++ b/quantum/xap/xap.c @@ -46,7 +46,6 @@ typedef enum xap_route_type_t { 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 @@ -92,11 +91,6 @@ bool xap_pre_execute_route(xap_token_t token, const xap_route_t *route) { 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 From 8874c664a83fdcdccba449edfdf0b0a19b4ef3be Mon Sep 17 00:00:00 2001 From: zvecr Date: Fri, 15 Jul 2022 01:45:58 +0100 Subject: [PATCH 113/203] Allow xap to compile without via.h --- quantum/dynamic_keymap.c | 15 ++++++++------- quantum/via.h | 2 ++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/quantum/dynamic_keymap.c b/quantum/dynamic_keymap.c index cbe9f13940db..e80dd6d534ec 100644 --- a/quantum/dynamic_keymap.c +++ b/quantum/dynamic_keymap.c @@ -19,7 +19,13 @@ #include "progmem.h" // to read default from flash #include "quantum.h" // for send_string() #include "dynamic_keymap.h" -#include "via.h" // for default VIA_EEPROM_ADDR_END + +#ifdef VIA_ENABLE +# include "via.h" // for VIA_EEPROM_CONFIG_END +# define DYNAMIC_KEYMAP_EEPROM_START (VIA_EEPROM_CONFIG_END) +#else +# define DYNAMIC_KEYMAP_EEPROM_START (EECONFIG_SIZE) +#endif #ifdef ENCODER_ENABLE # include "encoder.h" @@ -55,13 +61,8 @@ #endif // If DYNAMIC_KEYMAP_EEPROM_ADDR not explicitly defined in config.h, -// default it start after VIA_EEPROM_CUSTOM_ADDR+VIA_EEPROM_CUSTOM_SIZE #ifndef DYNAMIC_KEYMAP_EEPROM_ADDR -# ifdef VIA_EEPROM_CUSTOM_CONFIG_ADDR -# define DYNAMIC_KEYMAP_EEPROM_ADDR (VIA_EEPROM_CUSTOM_CONFIG_ADDR + VIA_EEPROM_CUSTOM_CONFIG_SIZE) -# else -# error DYNAMIC_KEYMAP_EEPROM_ADDR not defined -# endif +# define DYNAMIC_KEYMAP_EEPROM_ADDR DYNAMIC_KEYMAP_EEPROM_START #endif // Dynamic encoders starts after dynamic keymaps diff --git a/quantum/via.h b/quantum/via.h index ac29a5890232..31d9c21af4a5 100644 --- a/quantum/via.h +++ b/quantum/via.h @@ -54,6 +54,8 @@ # define VIA_EEPROM_CUSTOM_CONFIG_SIZE 0 #endif +#define VIA_EEPROM_CONFIG_END (VIA_EEPROM_CUSTOM_CONFIG_ADDR + VIA_EEPROM_CUSTOM_CONFIG_SIZE) + // This is changed only when the command IDs change, // so VIA Configurator can detect compatible firmware. #define VIA_PROTOCOL_VERSION 0x0009 From 28855f658adbb0b4f3803e960d5c9389ef7da332 Mon Sep 17 00:00:00 2001 From: zvecr Date: Fri, 15 Jul 2022 03:33:34 +0100 Subject: [PATCH 114/203] Hash dynamic keymap at runtime --- builddefs/build_keyboard.mk | 9 +---- builddefs/common_features.mk | 30 ++++++++------- lib/python/qmk/cli/__init__.py | 1 - lib/python/qmk/cli/generate/keymap_hash.py | 36 ------------------ quantum/dynamic_keymap.c | 43 ++++++++++++++++++++++ quantum/dynamic_keymap.h | 3 ++ quantum/eeconfig.c | 7 ++-- 7 files changed, 67 insertions(+), 62 deletions(-) delete mode 100644 lib/python/qmk/cli/generate/keymap_hash.py diff --git a/builddefs/build_keyboard.mk b/builddefs/build_keyboard.mk index 96a1dc8f8a92..5256e60adba6 100644 --- a/builddefs/build_keyboard.mk +++ b/builddefs/build_keyboard.mk @@ -1,4 +1,4 @@ -# Determine what keyboard we are building and setup the build environment. + # Determine what keyboard we are building and setup the build environment. # # We support folders up to 5 levels deep below `keyboards/`. This file is # responsible for determining which folder is being used and doing the @@ -352,12 +352,7 @@ $(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) -$(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 +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 .INTERMEDIATE : generated-files diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk index 85382e875480..a49dc6e016fe 100644 --- a/builddefs/common_features.mk +++ b/builddefs/common_features.mk @@ -610,6 +610,22 @@ ifeq ($(strip $(VIA_ENABLE)), yes) OPT_DEFS += -DVIA_ENABLE 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 + + DYNAMIC_KEYMAP_ENABLE := yes + FNV_ENABLE := yes + SECURE_ENABLE := yes + BOOTMAGIC_ENABLE := yes + + OPT_DEFS += -DXAP_ENABLE + OPT_DEFS += -DBOOTLOADER_JUMP_SUPPORTED + VPATH += $(QUANTUM_DIR)/xap + SRC += $(QUANTUM_DIR)/xap/xap.c $(QUANTUM_DIR)/xap/xap_handlers.c +endif + VALID_MAGIC_TYPES := yes BOOTMAGIC_ENABLE ?= no ifneq ($(strip $(BOOTMAGIC_ENABLE)), no) @@ -872,20 +888,6 @@ 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) diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py index 721927a631d7..fa26de65393f 100644 --- a/lib/python/qmk/cli/__init__.py +++ b/lib/python/qmk/cli/__init__.py @@ -55,7 +55,6 @@ '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', diff --git a/lib/python/qmk/cli/generate/keymap_hash.py b/lib/python/qmk/cli/generate/keymap_hash.py deleted file mode 100644 index 4a0bff83a46d..000000000000 --- a/lib/python/qmk/cli/generate/keymap_hash.py +++ /dev/null @@ -1,36 +0,0 @@ -"""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) diff --git a/quantum/dynamic_keymap.c b/quantum/dynamic_keymap.c index e80dd6d534ec..0ad2627a6a1c 100644 --- a/quantum/dynamic_keymap.c +++ b/quantum/dynamic_keymap.c @@ -19,6 +19,7 @@ #include "progmem.h" // to read default from flash #include "quantum.h" // for send_string() #include "dynamic_keymap.h" +#include "fnv.h" #ifdef VIA_ENABLE # include "via.h" // for VIA_EEPROM_CONFIG_END @@ -147,6 +148,47 @@ void dynamic_keymap_set_encoder(uint8_t layer, uint8_t encoder_id, bool clockwis } #endif // ENCODER_MAP_ENABLE +static uint32_t dynamic_keymap_compute_hash(void) { + Fnv32_t hash = FNV1_32A_INIT; + + uint16_t keycode; + for (int layer = 0; layer < DYNAMIC_KEYMAP_LAYER_COUNT; layer++) { + for (int row = 0; row < MATRIX_ROWS; row++) { + for (int column = 0; column < MATRIX_COLS; column++) { + keycode = pgm_read_word(&keymaps[layer][row][column]); + hash = fnv_32a_buf(&keycode, sizeof(keycode), hash); + } + } +#ifdef ENCODER_MAP_ENABLE + for (int encoder = 0; encoder < NUM_ENCODERS; encoder++) { + keycode = pgm_read_word(&encoder_map[layer][encoder][0]); + hash = fnv_32a_buf(&keycode, sizeof(keycode), hash); + + keycode = pgm_read_word(&encoder_map[layer][encoder][1]); + hash = fnv_32a_buf(&keycode, sizeof(keycode), hash); + } +#endif // ENCODER_MAP_ENABLE + } + + return hash; +} + +static uint32_t dynamic_keymap_hash(void) { + static uint32_t hash = 0; + + static uint8_t s_init = 0; + if (!s_init) { + s_init = 1; + + hash = dynamic_keymap_compute_hash(); + } + return hash; +} + +bool dynamic_keymap_is_valid(void) { + return eeprom_read_dword(EECONFIG_KEYMAP_HASH) == dynamic_keymap_hash(); +} + void dynamic_keymap_reset(void) { // Reset the keymaps in EEPROM to what is in flash. // All keyboards using dynamic keymaps should define a layout @@ -164,6 +206,7 @@ void dynamic_keymap_reset(void) { } #endif // ENCODER_MAP_ENABLE } + eeprom_update_dword(EECONFIG_KEYMAP_HASH, dynamic_keymap_hash()); } void dynamic_keymap_get_buffer(uint16_t offset, uint16_t size, uint8_t *data) { diff --git a/quantum/dynamic_keymap.h b/quantum/dynamic_keymap.h index 459b48d07a40..b5d183f5422a 100644 --- a/quantum/dynamic_keymap.h +++ b/quantum/dynamic_keymap.h @@ -26,7 +26,10 @@ void dynamic_keymap_set_keycode(uint8_t layer, uint8_t row, uint8_t column, uint16_t dynamic_keymap_get_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise); void dynamic_keymap_set_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise, uint16_t keycode); #endif // ENCODER_MAP_ENABLE + +bool dynamic_keymap_is_valid(void); void dynamic_keymap_reset(void); + // These get/set the keycodes as stored in the EEPROM buffer // Data is big-endian 16-bit values (the keycodes) // Order is by layer/row/column diff --git a/quantum/eeconfig.c b/quantum/eeconfig.c index 0698b1bd14a5..18748dbfead3 100644 --- a/quantum/eeconfig.c +++ b/quantum/eeconfig.c @@ -17,7 +17,7 @@ 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" +bool dynamic_keymap_is_valid(void); void dynamic_keymap_reset(void); #endif @@ -86,7 +86,6 @@ void eeconfig_init_quantum(void) { eeconfig_init_via(); #elif defined(DYNAMIC_KEYMAP_ENABLE) dynamic_keymap_reset(); - eeprom_update_dword(EECONFIG_KEYMAP_HASH, KEYMAP_HASH); #endif eeconfig_init_kb(); @@ -131,7 +130,7 @@ bool eeconfig_is_enabled(void) { } #elif defined(DYNAMIC_KEYMAP_ENABLE) if (is_eeprom_enabled) { - is_eeprom_enabled = (eeprom_read_dword(EECONFIG_KEYMAP_HASH) == KEYMAP_HASH); + is_eeprom_enabled = dynamic_keymap_is_valid(); } #endif return is_eeprom_enabled; @@ -149,7 +148,7 @@ bool eeconfig_is_disabled(void) { } #elif defined(DYNAMIC_KEYMAP_ENABLE) if (!is_eeprom_disabled) { - is_eeprom_disabled = (eeprom_read_dword(EECONFIG_KEYMAP_HASH) != KEYMAP_HASH); + is_eeprom_disabled = !dynamic_keymap_is_valid(); } #endif return is_eeprom_disabled; From 70eae6b34842141d5aca7dfcf04f4384c183235c Mon Sep 17 00:00:00 2001 From: zvecr Date: Sun, 17 Jul 2022 01:28:54 +0100 Subject: [PATCH 115/203] Move client to own folder --- lib/python/qmk/cli/xap/xap.py | 2 +- lib/python/xap_client/__init__.py | 2 + lib/python/xap_client/client.py | 29 +++++++++ .../xap_client.py => xap_client/device.py} | 63 ++----------------- lib/python/xap_client/types.py | 23 +++++++ 5 files changed, 60 insertions(+), 59 deletions(-) create mode 100644 lib/python/xap_client/__init__.py create mode 100644 lib/python/xap_client/client.py rename lib/python/{qmk/cli/xap/xap_client.py => xap_client/device.py} (79%) create mode 100644 lib/python/xap_client/types.py diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index be86e0a6f0bf..7757f8703a09 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -7,7 +7,7 @@ from qmk.keyboard import render_layout from qmk.xap.common import get_xap_keycodes -from .xap_client import XAPClient, XAPEventType, XAPSecureStatus +from xap_client import XAPClient, XAPEventType, XAPSecureStatus KEYCODE_MAP = get_xap_keycodes('latest') diff --git a/lib/python/xap_client/__init__.py b/lib/python/xap_client/__init__.py new file mode 100644 index 000000000000..e88170517e42 --- /dev/null +++ b/lib/python/xap_client/__init__.py @@ -0,0 +1,2 @@ +from .types import * # noqa: F403 +from .client import * # noqa: F403 diff --git a/lib/python/xap_client/client.py b/lib/python/xap_client/client.py new file mode 100644 index 000000000000..e3f032f4af90 --- /dev/null +++ b/lib/python/xap_client/client.py @@ -0,0 +1,29 @@ +"""XAP Client +""" +import hid + +from .device import XAPDevice + + +class XAPClient: + @staticmethod + def list(search=None): + """Find compatible XAP devices + """ + 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 + """ + return XAPDevice(dev) diff --git a/lib/python/qmk/cli/xap/xap_client.py b/lib/python/xap_client/device.py similarity index 79% rename from lib/python/qmk/cli/xap/xap_client.py rename to lib/python/xap_client/device.py index a12f54e35bc1..5d00a1e317a7 100644 --- a/lib/python/qmk/cli/xap/xap_client.py +++ b/lib/python/xap_client/device.py @@ -1,5 +1,6 @@ -"""Dummy XAP Client +"""XAP Device """ +import hid import json import random import gzip @@ -7,9 +8,11 @@ import functools from struct import Struct, pack, unpack from collections import namedtuple -from enum import IntFlag, IntEnum from platform import platform +from .types import XAPSecureStatus, XAPFlags, XAPRouteError + + RequestPacket = namedtuple('RequestPacket', 'token length data') RequestStruct = Struct('>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 - - -class XAPEventType(IntEnum): - SECURE = 0x01 - KEYBOARD = 0x02 - USER = 0x03 - - -class XAPRouteError(Exception): - pass - - class XAPDevice: def __init__(self, dev): """Constructor opens hid device and starts dependent services @@ -191,37 +172,3 @@ def lock(self): 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) diff --git a/lib/python/xap_client/types.py b/lib/python/xap_client/types.py new file mode 100644 index 000000000000..376849c8bb87 --- /dev/null +++ b/lib/python/xap_client/types.py @@ -0,0 +1,23 @@ +from enum import IntFlag, IntEnum + + +class XAPSecureStatus(IntEnum): + LOCKED = 0x00 + UNLOCKING = 0x01 + UNLOCKED = 0x02 + + +class XAPFlags(IntFlag): + FAILURE = 0 + SUCCESS = 1 << 0 + SECURE_FAILURE = 1 << 1 + + +class XAPEventType(IntEnum): + SECURE = 0x01 + KEYBOARD = 0x02 + USER = 0x03 + + +class XAPRouteError(Exception): + pass From 2a1bfafa1a7d9db09d27593d50d0aef38112cb0a Mon Sep 17 00:00:00 2001 From: zvecr Date: Sun, 17 Jul 2022 01:53:59 +0100 Subject: [PATCH 116/203] Stub out python client gen --- data/templates/xap/client/python/types.py.j2 | 25 ++++++++++ lib/python/qmk/cli/__init__.py | 1 + lib/python/qmk/cli/xap/generate_python.py | 22 +++++++++ lib/python/qmk/cli/xap/xap.py | 2 +- lib/python/xap_client/device.py | 1 - lib/python/xap_client/types.py | 48 ++++++++++++++++---- 6 files changed, 87 insertions(+), 12 deletions(-) create mode 100644 data/templates/xap/client/python/types.py.j2 create mode 100644 lib/python/qmk/cli/xap/generate_python.py diff --git a/data/templates/xap/client/python/types.py.j2 b/data/templates/xap/client/python/types.py.j2 new file mode 100644 index 000000000000..be9b386e1438 --- /dev/null +++ b/data/templates/xap/client/python/types.py.j2 @@ -0,0 +1,25 @@ +from enum import IntFlag, IntEnum + + +class XAPRouteError(Exception): + pass + + +class XAPSecureStatus(IntEnum): + LOCKED = 0x00 + UNLOCKING = 0x01 + UNLOCKED = 0x02 + + +class XAPEventType(IntEnum): +{%- for id, message in xap.broadcast_messages.messages | dictsort %} + {{ message.define }} = {{ id }} +{%- endfor %} + + +class XAPFlags(IntFlag): +{%- for bitnum, bitinfo in xap.response_flags.bits | dictsort %} +{%- if bitinfo.define != "-" %} + {{ bitinfo.define }} = 1 << {{ bitnum }} +{%- endif %} +{%- endfor %} diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py index fa26de65393f..ee6e86a56eda 100644 --- a/lib/python/qmk/cli/__init__.py +++ b/lib/python/qmk/cli/__init__.py @@ -80,6 +80,7 @@ 'qmk.cli.xap', 'qmk.cli.xap.generate_docs', 'qmk.cli.xap.generate_json', + 'qmk.cli.xap.generate_python', 'qmk.cli.xap.generate_qmk', ] diff --git a/lib/python/qmk/cli/xap/generate_python.py b/lib/python/qmk/cli/xap/generate_python.py new file mode 100644 index 000000000000..8dcf3b3eeca3 --- /dev/null +++ b/lib/python/qmk/cli/xap/generate_python.py @@ -0,0 +1,22 @@ +"""This script generates the python XAP client. +""" +from qmk.constants import QMK_FIRMWARE, GPL2_HEADER_SH_LIKE, GENERATED_HEADER_SH_LIKE + +from qmk.xap.common import latest_xap_defs, render_xap_output +from qmk.commands import dump_lines + +from milc import cli + + +@cli.subcommand('Generates the python XAP client.', hidden=False if cli.config.user.developer else True) +def xap_generate_python(cli): + defs = latest_xap_defs() + + parent = QMK_FIRMWARE / 'lib' / 'python' / 'xap_client' + for name in ['types.py']: + lines = [GPL2_HEADER_SH_LIKE, GENERATED_HEADER_SH_LIKE] + + output = render_xap_output('client/python', f'{name}.j2', defs) + lines += output.split('\n') + + dump_lines(parent / name, lines) diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index 7757f8703a09..668cbb155b4e 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -102,7 +102,7 @@ def do_listen(self, arg): while 1: (event, data) = self.device.listen() - if event == XAPEventType.SECURE: + if event == XAPEventType.SECURE_STATUS: secure_status = XAPSecureStatus(data[0]).name cli.log.info(' Secure[%s]', secure_status) diff --git a/lib/python/xap_client/device.py b/lib/python/xap_client/device.py index 5d00a1e317a7..52d13db8a53b 100644 --- a/lib/python/xap_client/device.py +++ b/lib/python/xap_client/device.py @@ -12,7 +12,6 @@ from .types import XAPSecureStatus, XAPFlags, XAPRouteError - RequestPacket = namedtuple('RequestPacket', 'token length data') RequestStruct = Struct(' Date: Sun, 17 Jul 2022 02:58:14 +0100 Subject: [PATCH 117/203] Stub out python client routes --- data/templates/xap/client/python/routes.py.j2 | 9 +++ lib/python/qmk/cli/xap/generate_python.py | 2 +- lib/python/xap_client/client.py | 4 +- lib/python/xap_client/device.py | 3 +- lib/python/xap_client/routes.py | 58 +++++++++++++++++++ 5 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 data/templates/xap/client/python/routes.py.j2 create mode 100644 lib/python/xap_client/routes.py diff --git a/data/templates/xap/client/python/routes.py.j2 b/data/templates/xap/client/python/routes.py.j2 new file mode 100644 index 000000000000..456d41f09842 --- /dev/null +++ b/data/templates/xap/client/python/routes.py.j2 @@ -0,0 +1,9 @@ +# TODO: assumption of only one level of children +{%- for id, route in xap.routes | dictsort %} +{%- if route.routes %} +# {{route.define}} +{%- for subid, subroute in route.routes | dictsort %} +{{route.define}}_{{subroute.define}} = b'\x{{ '%02d' % id|int(base=16) }}\x{{ '%02d' % subid|int(base=16) }}' +{%- endfor %} +{%- endif %} +{%- endfor %} \ No newline at end of file diff --git a/lib/python/qmk/cli/xap/generate_python.py b/lib/python/qmk/cli/xap/generate_python.py index 8dcf3b3eeca3..c761f7d8247d 100644 --- a/lib/python/qmk/cli/xap/generate_python.py +++ b/lib/python/qmk/cli/xap/generate_python.py @@ -13,7 +13,7 @@ def xap_generate_python(cli): defs = latest_xap_defs() parent = QMK_FIRMWARE / 'lib' / 'python' / 'xap_client' - for name in ['types.py']: + for name in ['types.py', 'routes.py']: lines = [GPL2_HEADER_SH_LIKE, GENERATED_HEADER_SH_LIKE] output = render_xap_output('client/python', f'{name}.j2', defs) diff --git a/lib/python/xap_client/client.py b/lib/python/xap_client/client.py index e3f032f4af90..6ab08a8e112b 100644 --- a/lib/python/xap_client/client.py +++ b/lib/python/xap_client/client.py @@ -2,8 +2,6 @@ """ import hid -from .device import XAPDevice - class XAPClient: @staticmethod @@ -26,4 +24,6 @@ def _is_filtered_device(x): def connect(self, dev): """Connect to a given XAP device """ + from .device import XAPDevice + return XAPDevice(dev) diff --git a/lib/python/xap_client/device.py b/lib/python/xap_client/device.py index 52d13db8a53b..c1f3ada33f1e 100644 --- a/lib/python/xap_client/device.py +++ b/lib/python/xap_client/device.py @@ -11,6 +11,7 @@ from platform import platform from .types import XAPSecureStatus, XAPFlags, XAPRouteError +from .routes import XAP_VERSION_QUERY RequestPacket = namedtuple('RequestPacket', 'token length data') RequestStruct = Struct(' Date: Sun, 17 Jul 2022 22:02:18 +0100 Subject: [PATCH 118/203] Tidy up python client --- data/templates/xap/client/python/routes.py.j2 | 7 ++- lib/python/qmk/cli/xap/xap.py | 8 +-- lib/python/xap_client/device.py | 49 +++++++++------ lib/python/xap_client/routes.py | 61 ++++++++++--------- 4 files changed, 68 insertions(+), 57 deletions(-) diff --git a/data/templates/xap/client/python/routes.py.j2 b/data/templates/xap/client/python/routes.py.j2 index 456d41f09842..7497e8afa1df 100644 --- a/data/templates/xap/client/python/routes.py.j2 +++ b/data/templates/xap/client/python/routes.py.j2 @@ -1,9 +1,10 @@ -# TODO: assumption of only one level of children + +class XAPRoutes(): {%- for id, route in xap.routes | dictsort %} {%- if route.routes %} -# {{route.define}} + # {{route.define}} {%- for subid, subroute in route.routes | dictsort %} -{{route.define}}_{{subroute.define}} = b'\x{{ '%02d' % id|int(base=16) }}\x{{ '%02d' % subid|int(base=16) }}' + {{route.define}}_{{subroute.define}} = b'\x{{ '%02d' % id|int(base=16) }}\x{{ '%02d' % subid|int(base=16) }}' {%- endfor %} {%- endif %} {%- endfor %} \ No newline at end of file diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index 668cbb155b4e..34f36c1ccc20 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -47,7 +47,7 @@ def _list_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']) + cli.log.info(' %04x:%04x %s %s [API:%s]', dev['vendor_id'], dev['product_id'], dev['manufacturer_string'], dev['product_string'], data['_version']['xap']) if cli.config.general.verbose: # TODO: better formatting like 'lsusb -v'? @@ -65,10 +65,10 @@ def __init__(self, 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 + """Prints out the version info of QMK """ - # TODO: request stuff? - print(self.device.info()['xap']) + data = self.device.version() + print_dotted_output(data) def do_status(self, arg): """Prints out the current device state diff --git a/lib/python/xap_client/device.py b/lib/python/xap_client/device.py index c1f3ada33f1e..4f728ae88838 100644 --- a/lib/python/xap_client/device.py +++ b/lib/python/xap_client/device.py @@ -2,8 +2,9 @@ """ import hid import json -import random +import time import gzip +import random import threading import functools from struct import Struct, pack, unpack @@ -11,7 +12,8 @@ from platform import platform from .types import XAPSecureStatus, XAPFlags, XAPRouteError -from .routes import XAP_VERSION_QUERY +from .routes import XAPRoutes +from .util import u32toBCD RequestPacket = namedtuple('RequestPacket', 'token length data') RequestStruct = Struct('H', token))[0] -def _u32toBCD(val): # noqa: N802 - """Create BCD string - """ - return f'{val>>24}.{val>>16 & 0xFF}.{val & 0xFFFF}' - - class XAPDevice: def __init__(self, dev): """Constructor opens hid device and starts dependent services """ self.responses = {} + self.do_read = True self.dev = hid.Device(path=dev['path']) self.bg = threading.Thread(target=self._read_loop, daemon=True) self.bg.start() + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, exc_traceback): + self.close() + + def close(self): + self.do_read = False + time.sleep(1) + self.dev.close() + def _read_loop(self): """Background thread to signal waiting transactions """ - while 1: + while self.do_read: array_alpha = self.dev.read(ResponseStruct.size, 100) if array_alpha: token = int.from_bytes(array_alpha[:2], 'little') @@ -59,14 +67,14 @@ def _read_loop(self): event.set() def _query_device_info(self): - datalen = int.from_bytes(self.transaction(b'\x01\x05') or bytes(0), 'little') + datalen = int.from_bytes(self.transaction(XAPRoutes.QMK_CONFIG_BLOB_LEN) or bytes(0), 'little') if not datalen: return {} data = [] offset = 0 while offset < datalen: - chunk = self.transaction(b'\x01\x06', offset) + chunk = self.transaction(XAPRoutes.QMK_CONFIG_BLOB_CHUNK, offset) data += chunk offset += len(chunk) str_data = gzip.decompress(bytearray(data[:datalen])) @@ -127,13 +135,14 @@ def capability(self, route): @functools.lru_cache def subsystem(self): - sub = int.from_bytes(self._transaction(b'\x00\x02') or bytes(0), 'little') + sub = int.from_bytes(self._transaction(XAPRoutes.XAP_SUBSYSTEM_QUERY) or bytes(0), 'little') return sub @functools.lru_cache def version(self): - ver = int.from_bytes(self._transaction(XAP_VERSION_QUERY) or bytes(0), 'little') - return {'xap': _u32toBCD(ver)} + xap = int.from_bytes(self._transaction(XAPRoutes.XAP_VERSION_QUERY) or bytes(0), 'little') + qmk = int.from_bytes(self._transaction(XAPRoutes.QMK_VERSION_QUERY) or bytes(0), 'little') + return {'xap': u32toBCD(xap), 'qmk': u32toBCD(qmk)} def _ensure_route(self, route): (sub, rt) = route @@ -152,23 +161,23 @@ def transaction(self, route, *args): @functools.lru_cache def info(self): data = self._query_device_info() - data['_id'] = self.transaction(b'\x01\x08') - data['xap'] = self.version()['xap'] + data['_id'] = self.transaction(XAPRoutes.QMK_HARDWARE_ID) + data['_version'] = self.version() return data def status(self): - lock = int.from_bytes(self.transaction(b'\x00\x03') or bytes(0), 'little') + lock = int.from_bytes(self.transaction(XAPRoutes.XAP_SECURE_STATUS) or bytes(0), 'little') data = {} data['lock'] = XAPSecureStatus(lock).name return data def unlock(self): - self.transaction(b'\x00\x04') + self.transaction(XAPRoutes.XAP_SECURE_UNLOCK) def lock(self): - self.transaction(b'\x00\x05') + self.transaction(XAPRoutes.XAP_SECURE_LOCK) def reset(self): - status = int.from_bytes(self.transaction(b'\x01\x07') or bytes(0), 'little') + status = int.from_bytes(self.transaction(XAPRoutes.QMK_BOOTLOADER_JUMP) or bytes(0), 'little') return status == 1 diff --git a/lib/python/xap_client/routes.py b/lib/python/xap_client/routes.py index 0cdb9a148d07..f7e67a20e325 100644 --- a/lib/python/xap_client/routes.py +++ b/lib/python/xap_client/routes.py @@ -26,33 +26,34 @@ # ################################################################################ -# TODO: assumption of only one level of children -# XAP -XAP_VERSION_QUERY = b'\x00\x00' -XAP_CAPABILITIES_QUERY = b'\x00\x01' -XAP_SUBSYSTEM_QUERY = b'\x00\x02' -XAP_SECURE_STATUS = b'\x00\x03' -XAP_SECURE_UNLOCK = b'\x00\x04' -XAP_SECURE_LOCK = b'\x00\x05' -# QMK -QMK_VERSION_QUERY = b'\x01\x00' -QMK_CAPABILITIES_QUERY = b'\x01\x01' -QMK_BOARD_IDENTIFIERS = b'\x01\x02' -QMK_BOARD_MANUFACTURER = b'\x01\x03' -QMK_PRODUCT_NAME = b'\x01\x04' -QMK_CONFIG_BLOB_LEN = b'\x01\x05' -QMK_CONFIG_BLOB_CHUNK = b'\x01\x06' -QMK_BOOTLOADER_JUMP = b'\x01\x07' -QMK_HARDWARE_ID = b'\x01\x08' -# KEYMAP -KEYMAP_CAPABILITIES_QUERY = b'\x04\x01' -KEYMAP_GET_LAYER_COUNT = b'\x04\x02' -KEYMAP_GET_KEYMAP_KEYCODE = b'\x04\x03' -KEYMAP_GET_ENCODER_KEYCODE = b'\x04\x04' -# REMAPPING -REMAPPING_CAPABILITIES_QUERY = b'\x05\x01' -REMAPPING_GET_DYNAMIC_LAYER_COUNT = b'\x05\x02' -REMAPPING_SET_KEYMAP_KEYCODE = b'\x05\x03' -REMAPPING_SET_ENCODER_KEYCODE = b'\x05\x04' -# LIGHTING -LIGHTING_CAPABILITIES_QUERY = b'\x06\x01' + +class XAPRoutes(): + # XAP + XAP_VERSION_QUERY = b'\x00\x00' + XAP_CAPABILITIES_QUERY = b'\x00\x01' + XAP_SUBSYSTEM_QUERY = b'\x00\x02' + XAP_SECURE_STATUS = b'\x00\x03' + XAP_SECURE_UNLOCK = b'\x00\x04' + XAP_SECURE_LOCK = b'\x00\x05' + # QMK + QMK_VERSION_QUERY = b'\x01\x00' + QMK_CAPABILITIES_QUERY = b'\x01\x01' + QMK_BOARD_IDENTIFIERS = b'\x01\x02' + QMK_BOARD_MANUFACTURER = b'\x01\x03' + QMK_PRODUCT_NAME = b'\x01\x04' + QMK_CONFIG_BLOB_LEN = b'\x01\x05' + QMK_CONFIG_BLOB_CHUNK = b'\x01\x06' + QMK_BOOTLOADER_JUMP = b'\x01\x07' + QMK_HARDWARE_ID = b'\x01\x08' + # KEYMAP + KEYMAP_CAPABILITIES_QUERY = b'\x04\x01' + KEYMAP_GET_LAYER_COUNT = b'\x04\x02' + KEYMAP_GET_KEYMAP_KEYCODE = b'\x04\x03' + KEYMAP_GET_ENCODER_KEYCODE = b'\x04\x04' + # REMAPPING + REMAPPING_CAPABILITIES_QUERY = b'\x05\x01' + REMAPPING_GET_DYNAMIC_LAYER_COUNT = b'\x05\x02' + REMAPPING_SET_KEYMAP_KEYCODE = b'\x05\x03' + REMAPPING_SET_ENCODER_KEYCODE = b'\x05\x04' + # LIGHTING + LIGHTING_CAPABILITIES_QUERY = b'\x06\x01' From 5c21830da2062a80069e5855e3e43148b4dc0e0d Mon Sep 17 00:00:00 2001 From: zvecr Date: Sun, 17 Jul 2022 22:03:53 +0100 Subject: [PATCH 119/203] Tidy up python client - add file --- lib/python/xap_client/util.py | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 lib/python/xap_client/util.py diff --git a/lib/python/xap_client/util.py b/lib/python/xap_client/util.py new file mode 100644 index 000000000000..f09d99e0bbd1 --- /dev/null +++ b/lib/python/xap_client/util.py @@ -0,0 +1,4 @@ +def u32toBCD(val): # noqa: N802 + """Create BCD string + """ + return f'{val>>24}.{val>>16 & 0xFF}.{val & 0xFFFF}' From 7bce3d7b25bf8a7585ee4d471f47ff8e758d76b5 Mon Sep 17 00:00:00 2001 From: zvecr Date: Sun, 17 Jul 2022 23:04:35 +0100 Subject: [PATCH 120/203] Prep client gen for header parsing --- data/templates/xap/client/python/routes.py.j2 | 4 +++ data/templates/xap/client/python/types.py.j2 | 30 +++++++++++++++++-- lib/python/xap_client/device.py | 30 +++++++------------ lib/python/xap_client/routes.py | 4 +++ lib/python/xap_client/types.py | 30 +++++++++++++++++-- 5 files changed, 75 insertions(+), 23 deletions(-) diff --git a/data/templates/xap/client/python/routes.py.j2 b/data/templates/xap/client/python/routes.py.j2 index 7497e8afa1df..7eb6a5bde2e2 100644 --- a/data/templates/xap/client/python/routes.py.j2 +++ b/data/templates/xap/client/python/routes.py.j2 @@ -1,4 +1,8 @@ +class XAPRouteError(Exception): + pass + + class XAPRoutes(): {%- for id, route in xap.routes | dictsort %} {%- if route.routes %} diff --git a/data/templates/xap/client/python/types.py.j2 b/data/templates/xap/client/python/types.py.j2 index be9b386e1438..b6202fa2777e 100644 --- a/data/templates/xap/client/python/types.py.j2 +++ b/data/templates/xap/client/python/types.py.j2 @@ -1,8 +1,34 @@ +from collections import namedtuple from enum import IntFlag, IntEnum +from struct import Struct -class XAPRouteError(Exception): - pass +class XAPRequest(namedtuple('XAPRequest', 'token length data')): + fmt = Struct(' Date: Mon, 18 Jul 2022 00:06:32 +0100 Subject: [PATCH 121/203] Prep client gen for header parsing - templates --- data/templates/xap/client/python/types.py.j2 | 28 ++++++-------------- lib/python/xap_client/types.py | 12 ++++----- 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/data/templates/xap/client/python/types.py.j2 b/data/templates/xap/client/python/types.py.j2 index b6202fa2777e..2509688abfdb 100644 --- a/data/templates/xap/client/python/types.py.j2 +++ b/data/templates/xap/client/python/types.py.j2 @@ -2,35 +2,23 @@ from collections import namedtuple from enum import IntFlag, IntEnum from struct import Struct - -class XAPRequest(namedtuple('XAPRequest', 'token length data')): - fmt = Struct(' Date: Tue, 19 Jul 2022 00:46:08 +0100 Subject: [PATCH 122/203] Tidy up code/docstrings --- lib/python/qmk/cli/xap/xap.py | 13 ++- lib/python/xap_client/__init__.py | 3 + lib/python/xap_client/client.py | 17 ++- lib/python/xap_client/device.py | 177 +++++++++++++++++++----------- lib/python/xap_client/readme.md | 18 +++ lib/python/xap_client/util.py | 4 - 6 files changed, 151 insertions(+), 81 deletions(-) create mode 100644 lib/python/xap_client/readme.md delete mode 100644 lib/python/xap_client/util.py diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index 34f36c1ccc20..c050a4f2e759 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -42,14 +42,14 @@ def _list_devices(): """Dump out available devices """ cli.log.info('Available devices:') - devices = XAPClient.list() - for dev in devices: + for dev in XAPClient.devices(): device = XAPClient().connect(dev) + ver = device.version() - 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['_version']['xap']) + cli.log.info(' %04x:%04x %s %s [API:%s]', dev['vendor_id'], dev['product_id'], dev['manufacturer_string'], dev['product_string'], ver['xap']) - if cli.config.general.verbose: + if cli.args.verbose: + data = device.info() # TODO: better formatting like 'lsusb -v'? print_dotted_output(data) @@ -188,6 +188,7 @@ def loop(self): return False +@cli.argument('-v', '--verbose', arg_only=True, action='store_true', help='Turns on verbose output.') @cli.argument('-d', '--device', help='device to select - uses format :.') @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.') @@ -200,7 +201,7 @@ def xap(cli): return _list_devices() # Connect to first available device - devices = XAPClient.list() + devices = XAPClient.devices() if not devices: cli.log.error('No devices found!') return False diff --git a/lib/python/xap_client/__init__.py b/lib/python/xap_client/__init__.py index e88170517e42..4c8affe1b88d 100644 --- a/lib/python/xap_client/__init__.py +++ b/lib/python/xap_client/__init__.py @@ -1,2 +1,5 @@ +# Copyright 2022 QMK +# SPDX-License-Identifier: GPL-2.0-or-later from .types import * # noqa: F403 from .client import * # noqa: F403 +from .routes import * # noqa: F403 diff --git a/lib/python/xap_client/client.py b/lib/python/xap_client/client.py index 6ab08a8e112b..92258b7594cd 100644 --- a/lib/python/xap_client/client.py +++ b/lib/python/xap_client/client.py @@ -1,12 +1,17 @@ -"""XAP Client -""" +# Copyright 2022 QMK +# SPDX-License-Identifier: GPL-2.0-or-later import hid class XAPClient: + """XAP device discovery + """ @staticmethod - def list(search=None): + def devices(search: str = None) -> list[dict]: """Find compatible XAP devices + + Args: + search: optional search string to filter results by """ def _is_xap_usage(x): return x['usage_page'] == 0xFF51 and x['usage'] == 0x0058 @@ -21,9 +26,11 @@ def _is_filtered_device(x): return list(devices) - def connect(self, dev): + def connect(self, device: dict): """Connect to a given XAP device + Args: + device: item from a previous `XAPClient.devices()` call """ from .device import XAPDevice - return XAPDevice(dev) + return XAPDevice(device) diff --git a/lib/python/xap_client/device.py b/lib/python/xap_client/device.py index 91feb91c6cd0..d9ffc17dc1b4 100644 --- a/lib/python/xap_client/device.py +++ b/lib/python/xap_client/device.py @@ -1,5 +1,5 @@ -"""XAP Device -""" +# Copyright 2022 QMK +# SPDX-License-Identifier: GPL-2.0-or-later import hid import json import time @@ -12,10 +12,15 @@ from .types import XAPSecureStatus, XAPFlags, XAPRequest, XAPResponse from .routes import XAPRoutes, XAPRouteError -from .util import u32toBCD -def _gen_token(): +def _u32_to_bcd(val: bytes) -> str: # noqa: N802 + """Create BCD string + """ + return f'{val>>24}.{val>>16 & 0xFF}.{val & 0xFFFF}' + + +def _gen_token() -> bytes: """Generate XAP token - cannot start with 00xx or 'reserved' (FFFE|FFFF) """ token = random.randrange(0x0100, 0xFFFD) @@ -24,33 +29,32 @@ def _gen_token(): return unpack('H', token))[0] -class XAPDevice: - def __init__(self, dev): +class XAPDeviceBase: + """Raw XAP interactions + """ + def __init__(self, dev: dict, timeout: int = 1.0): """Constructor opens hid device and starts dependent services """ self.responses = {} - self.do_read = True + self.timeout = timeout + self.running = True self.dev = hid.Device(path=dev['path']) self.bg = threading.Thread(target=self._read_loop, daemon=True) self.bg.start() - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_value, exc_traceback): - self.close() - def close(self): - self.do_read = False + """Close device and stop dependent services + """ + self.running = False time.sleep(1) self.dev.close() def _read_loop(self): """Background thread to signal waiting transactions """ - while self.do_read: + while self.running: data = self.dev.read(XAPResponse.fmt.size, 100) if data: r = XAPResponse.from_bytes(data) @@ -59,35 +63,8 @@ def _read_loop(self): event._ret = data event.set() - def _query_device_info(self): - datalen = int.from_bytes(self.transaction(XAPRoutes.QMK_CONFIG_BLOB_LEN) or bytes(0), 'little') - if not datalen: - return {} - - data = [] - offset = 0 - while offset < datalen: - chunk = self.transaction(XAPRoutes.QMK_CONFIG_BLOB_CHUNK, 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 - - while not hasattr(event, '_ret'): - event.wait(timeout=0.25) - - r = XAPResponse.from_bytes(event._ret) - return (r.flags, r.data[:r.length]) - - def _transaction(self, *args): - """Request/Receive + def transaction(self, *args) -> bytes | None: + """Request/Receive Helper """ # convert args to array of bytes data = bytes() @@ -109,7 +86,7 @@ def _transaction(self, *args): buffer = b'\x00' + buffer self.dev.write(buffer) - event.wait(timeout=1) + event.wait(timeout=self.timeout) self.responses.pop(token, None) if not hasattr(event, '_ret'): return None @@ -120,56 +97,124 @@ def _transaction(self, *args): return r.data[:r.length] - @functools.lru_cache - def capability(self, route): - cap = int.from_bytes(self._transaction(route) or bytes(0), 'little') - return cap + def listen(self) -> dict: + """Receive a single 'broadcast' message + """ + token = 0xFFFF + event = threading.Event() + self.responses[token] = event - @functools.lru_cache - def subsystem(self): - sub = int.from_bytes(self._transaction(XAPRoutes.XAP_SUBSYSTEM_QUERY) or bytes(0), 'little') - return sub + # emulate a blocking read while allowing `ctrl+c` on windows + while not hasattr(event, '_ret'): + event.wait(timeout=0.25) - @functools.lru_cache - def version(self): - xap = int.from_bytes(self._transaction(XAPRoutes.XAP_VERSION_QUERY) or bytes(0), 'little') - qmk = int.from_bytes(self._transaction(XAPRoutes.QMK_VERSION_QUERY) or bytes(0), 'little') - return {'xap': u32toBCD(xap), 'qmk': u32toBCD(qmk)} + r = XAPResponse.from_bytes(event._ret) + return (r.flags, r.data[:r.length]) + + +class XAPDevice(XAPDeviceBase): + """XAP device interaction + """ + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, exc_traceback): + self.close() + + def _query_device_info(self) -> dict: + """Helper to reconstruct info.json from requested chunks + """ + datalen = self.int_transaction(XAPRoutes.QMK_CONFIG_BLOB_LEN) + if not datalen: + return {} + + data = [] + offset = 0 + while offset < datalen: + chunk = self.transaction(XAPRoutes.QMK_CONFIG_BLOB_CHUNK, offset) + data += chunk + offset += len(chunk) + str_data = gzip.decompress(bytearray(data[:datalen])) + return json.loads(str_data) - def _ensure_route(self, route): + def _ensure_route(self, route: bytes): + """Check a route can be accessed + + Raises: + XAPRouteError: Access to invalid route attempted + """ + # TODO: Remove assumption that capability is always xx01 (sub, rt) = route cap = bytes([sub, 1]) - if self.subsystem() & (1 << sub) == 0: + if self.subsystems() & (1 << sub) == 0: raise XAPRouteError("subsystem not available") if self.capability(cap) & (1 << rt) == 0: raise XAPRouteError("route not available") - def transaction(self, route, *args): + def transaction(self, route: bytes, *args): + """Request/Receive to XAP device + + Raises: + XAPRouteError: Access to invalid route attempted + """ self._ensure_route(route) - return self._transaction(route, *args) + return super().transaction(route, *args) + + def int_transaction(self, route: bytes, *args): + """transaction with int parsing + """ + return int.from_bytes(self.transaction(route, *args) or bytes(0), 'little') + + @functools.lru_cache + def capability(self, route: bytes): + # use parent transaction as we want to ignore capability checks + return int.from_bytes(super().transaction(route) or bytes(0), 'little') @functools.lru_cache - def info(self): + def subsystems(self): + # use parent transaction as we want to ignore capability checks + return int.from_bytes(super().transaction(XAPRoutes.XAP_SUBSYSTEM_QUERY) or bytes(0), 'little') + + @functools.lru_cache + def version(self) -> dict: + """Query version data from device + """ + xap = self.int_transaction(XAPRoutes.XAP_VERSION_QUERY) + qmk = self.int_transaction(XAPRoutes.QMK_VERSION_QUERY) + return {'xap': _u32_to_bcd(xap), 'qmk': _u32_to_bcd(qmk)} + + @functools.lru_cache + def info(self) -> dict: + """Query config data from device + """ data = self._query_device_info() data['_id'] = self.transaction(XAPRoutes.QMK_HARDWARE_ID) data['_version'] = self.version() return data - def status(self): - lock = int.from_bytes(self.transaction(XAPRoutes.XAP_SECURE_STATUS) or bytes(0), 'little') + def status(self) -> dict: + """Query current device state + """ + lock = self.int_transaction(XAPRoutes.XAP_SECURE_STATUS) data = {} data['lock'] = XAPSecureStatus(lock).name return data def unlock(self): + """Initiate unlock procedure + """ self.transaction(XAPRoutes.XAP_SECURE_UNLOCK) def lock(self): + """Lock device + """ self.transaction(XAPRoutes.XAP_SECURE_LOCK) def reset(self): - status = int.from_bytes(self.transaction(XAPRoutes.QMK_BOOTLOADER_JUMP) or bytes(0), 'little') + """Request device reboot to bootloader - Requires previous unlock + """ + status = self.int_transaction(XAPRoutes.QMK_BOOTLOADER_JUMP) return status == 1 diff --git a/lib/python/xap_client/readme.md b/lib/python/xap_client/readme.md new file mode 100644 index 000000000000..9dce04721a1f --- /dev/null +++ b/lib/python/xap_client/readme.md @@ -0,0 +1,18 @@ +# XAP python3 bindings + +## Example +```python +from xap_client import XAPClient + +# List Available Devices +devices = XAPClient.devices() +selected = devices[0] + +# Connect then run commands +with XAPClient().connect(selected) as dev: + print(dev.version()) +``` + +## API + +TODO diff --git a/lib/python/xap_client/util.py b/lib/python/xap_client/util.py deleted file mode 100644 index f09d99e0bbd1..000000000000 --- a/lib/python/xap_client/util.py +++ /dev/null @@ -1,4 +0,0 @@ -def u32toBCD(val): # noqa: N802 - """Create BCD string - """ - return f'{val>>24}.{val>>16 & 0xFF}.{val & 0xFFFF}' From f4f2002ed87fd7f557c686a124564e489588cf4e Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 19 Jul 2022 02:14:53 +0100 Subject: [PATCH 123/203] Patch up version encoding --- lib/python/qmk/xap/gen_firmware/header_generator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/python/qmk/xap/gen_firmware/header_generator.py b/lib/python/qmk/xap/gen_firmware/header_generator.py index 423e9e65d36e..bb005e159801 100755 --- a/lib/python/qmk/xap/gen_firmware/header_generator.py +++ b/lib/python/qmk/xap/gen_firmware/header_generator.py @@ -244,9 +244,9 @@ def generate_header(output_file, keyboard, keymap): # 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') + lines.append(f'#define XAP_BCD_VERSION 0x{int(b.group(1)):02X}{int(b.group(2)):02X}{int(b.group(3)):04X}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') + lines.append(f'#define QMK_BCD_VERSION 0x{int(b[0][0]):02X}{int(b[0][1]):02X}{int(b[0][2]):04X}ul') keyboard_id = fnv1a_32(bytes(keyboard, 'utf-8')) lines.append(f'#define XAP_KEYBOARD_IDENTIFIER 0x{keyboard_id:08X}ul') lines.append('') From 81ac1a5df5a4b807fabca4a5234b0f5156eb43d6 Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 28 Jul 2022 01:42:32 +0100 Subject: [PATCH 124/203] Patch up fnv use in dynamic keymap --- quantum/dynamic_keymap.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/quantum/dynamic_keymap.c b/quantum/dynamic_keymap.c index 1f4ad78ea547..76f4560573c9 100644 --- a/quantum/dynamic_keymap.c +++ b/quantum/dynamic_keymap.c @@ -19,7 +19,10 @@ #include "progmem.h" // to read default from flash #include "quantum.h" // for send_string() #include "dynamic_keymap.h" -#include "fnv.h" + +#ifdef FNV_ENABLE +# include "fnv.h" +#endif #ifdef VIA_ENABLE # include "via.h" // for VIA_EEPROM_CONFIG_END @@ -149,6 +152,7 @@ void dynamic_keymap_set_encoder(uint8_t layer, uint8_t encoder_id, bool clockwis #endif // ENCODER_MAP_ENABLE static uint32_t dynamic_keymap_compute_hash(void) { +#ifdef FNV_ENABLE Fnv32_t hash = FNV1_32A_INIT; uint16_t keycode; @@ -159,7 +163,7 @@ static uint32_t dynamic_keymap_compute_hash(void) { hash = fnv_32a_buf(&keycode, sizeof(keycode), hash); } } -#ifdef ENCODER_MAP_ENABLE +# ifdef ENCODER_MAP_ENABLE for (int encoder = 0; encoder < NUM_ENCODERS; encoder++) { keycode = pgm_read_word(&encoder_map[layer][encoder][0]); hash = fnv_32a_buf(&keycode, sizeof(keycode), hash); @@ -167,10 +171,12 @@ static uint32_t dynamic_keymap_compute_hash(void) { keycode = pgm_read_word(&encoder_map[layer][encoder][1]); hash = fnv_32a_buf(&keycode, sizeof(keycode), hash); } -#endif // ENCODER_MAP_ENABLE +# endif // ENCODER_MAP_ENABLE } - return hash; +#else + return 0; +#endif } static uint32_t dynamic_keymap_hash(void) { From e1e3e6b5992591f45b88ff9d236fa5afb556a214 Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 2 Aug 2022 03:48:06 +0100 Subject: [PATCH 125/203] strip out some additional info.json items --- lib/python/qmk/xap/gen_firmware/blob_generator.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/python/qmk/xap/gen_firmware/blob_generator.py b/lib/python/qmk/xap/gen_firmware/blob_generator.py index 3d592426657b..a426d21baddf 100644 --- a/lib/python/qmk/xap/gen_firmware/blob_generator.py +++ b/lib/python/qmk/xap/gen_firmware/blob_generator.py @@ -23,6 +23,9 @@ def _build_info(keyboard, keymap): # TODO: Munge to XAP requirements del info_json['config_h_features'] + del info_json['keymaps'] + del info_json['parse_errors'] + del info_json['parse_warnings'] return info_json From 507d65eb1b58e65af3b813a3ff328851e26a8e89 Mon Sep 17 00:00:00 2001 From: zvecr Date: Sun, 7 Aug 2022 22:51:20 +0100 Subject: [PATCH 126/203] Strip more from embedded info.json --- lib/python/qmk/xap/gen_firmware/blob_generator.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/python/qmk/xap/gen_firmware/blob_generator.py b/lib/python/qmk/xap/gen_firmware/blob_generator.py index a426d21baddf..94974d911345 100644 --- a/lib/python/qmk/xap/gen_firmware/blob_generator.py +++ b/lib/python/qmk/xap/gen_firmware/blob_generator.py @@ -26,6 +26,12 @@ def _build_info(keyboard, keymap): del info_json['keymaps'] del info_json['parse_errors'] del info_json['parse_warnings'] + del info_json['usb']['device_ver'] + for layout in info_json['layouts'].values(): + del layout['filename'] + del layout['c_macro'] + for item in layout['layout']: + item.pop('label', None) return info_json From 3fe1a931d7c58fbd13759612a68e61582c2e36da Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 9 Aug 2022 19:10:36 +0100 Subject: [PATCH 127/203] Fix up OOB behaviour for get/set keycodes --- quantum/xap/xap_handlers.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/quantum/xap/xap_handlers.c b/quantum/xap/xap_handlers.c index 1d99cff875ae..e02b746d2d67 100644 --- a/quantum/xap/xap_handlers.c +++ b/quantum/xap/xap_handlers.c @@ -25,6 +25,12 @@ # define secure_lock() #endif +#ifdef DYNAMIC_KEYMAP_ENABLE +# define keymap_max_layer_count() DYNAMIC_KEYMAP_LAYER_COUNT +#else +# define keymap_max_layer_count() keymap_layer_count() +#endif + void xap_respond_success(xap_token_t token) { xap_send(token, XAP_RESPONSE_FLAG_SUCCESS, NULL, 0); } @@ -100,7 +106,7 @@ bool xap_respond_get_hardware_id(xap_token_t token, const void *data, size_t len } bool xap_respond_keymap_get_layer_count(xap_token_t token, const void *data, size_t length) { - uint8_t ret = keymap_layer_count(); + uint8_t ret = keymap_max_layer_count(); return xap_respond_data(token, &ret, sizeof(ret)); } @@ -111,6 +117,10 @@ bool xap_respond_get_keymap_keycode(xap_token_t token, const void *data, size_t xap_route_keymap_get_keymap_keycode_arg_t *arg = (xap_route_keymap_get_keymap_keycode_arg_t *)data; + if (arg->layer >= keymap_max_layer_count()) { + return false; + } + keypos_t pos = MAKE_KEYPOS(arg->row, arg->column); uint16_t keycode = keymap_key_to_keycode(arg->layer, pos); @@ -125,6 +135,10 @@ bool xap_respond_get_encoder_keycode(xap_token_t token, const void *data, size_t xap_route_keymap_get_encoder_keycode_arg_t *arg = (xap_route_keymap_get_encoder_keycode_arg_t *)data; + if (arg->layer >= keymap_max_layer_count()) { + return false; + } + 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); @@ -140,6 +154,10 @@ bool xap_respond_dynamic_keymap_set_keycode(xap_token_t token, const void *data, xap_route_remapping_set_keymap_keycode_arg_t *arg = (xap_route_remapping_set_keymap_keycode_arg_t *)data; + if (arg->layer >= keymap_max_layer_count()) { + return false; + } + dynamic_keymap_set_keycode(arg->layer, arg->row, arg->column, arg->keycode); xap_respond_success(token); return true; @@ -154,6 +172,10 @@ bool xap_respond_dynamic_encoder_set_keycode(xap_token_t token, const void *data xap_route_remapping_set_encoder_keycode_arg_t *arg = (xap_route_remapping_set_encoder_keycode_arg_t *)data; + if (arg->layer >= keymap_max_layer_count()) { + return false; + } + dynamic_keymap_set_encoder(arg->layer, arg->encoder, arg->clockwise, arg->keycode); xap_respond_success(token); return true; From b0756d18c7113435bd317195733cefcec3c757e3 Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 9 Aug 2022 23:53:10 +0100 Subject: [PATCH 128/203] Be more defensive in blob gen --- .../qmk/xap/gen_firmware/blob_generator.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/python/qmk/xap/gen_firmware/blob_generator.py b/lib/python/qmk/xap/gen_firmware/blob_generator.py index 94974d911345..be137571d542 100644 --- a/lib/python/qmk/xap/gen_firmware/blob_generator.py +++ b/lib/python/qmk/xap/gen_firmware/blob_generator.py @@ -22,15 +22,15 @@ def _build_info(keyboard, keymap): deep_update(info_json, km_info_json) # TODO: Munge to XAP requirements - del info_json['config_h_features'] - del info_json['keymaps'] - del info_json['parse_errors'] - del info_json['parse_warnings'] - del info_json['usb']['device_ver'] - for layout in info_json['layouts'].values(): - del layout['filename'] - del layout['c_macro'] - for item in layout['layout']: + info_json.pop('config_h_features', None) + info_json.pop('keymaps', None) + info_json.pop('parse_errors', None) + info_json.pop('parse_warnings', None) + info_json.get('usb', {}).pop('device_ver', None) + for layout in info_json.get('layouts', {}).values(): + layout.pop('filename', None) + layout.pop('c_macro', None) + for item in layout.get('layout', []): item.pop('label', None) return info_json From 198e761173b658aee74a25bbc9dd1eb9a49b05d5 Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 30 Aug 2022 18:47:55 +0100 Subject: [PATCH 129/203] Remove 3.10+ type union --- lib/python/xap_client/device.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/python/xap_client/device.py b/lib/python/xap_client/device.py index d9ffc17dc1b4..bd7f5691fb95 100644 --- a/lib/python/xap_client/device.py +++ b/lib/python/xap_client/device.py @@ -7,6 +7,7 @@ import random import threading import functools +from typing import Optional from struct import pack, unpack from platform import platform @@ -63,7 +64,7 @@ def _read_loop(self): event._ret = data event.set() - def transaction(self, *args) -> bytes | None: + def transaction(self, *args) -> Optional[bytes]: """Request/Receive Helper """ # convert args to array of bytes From 8e18f4d0fd24f890fdbff47b2ae8fa9b69c88076 Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 6 Sep 2022 17:33:19 +0100 Subject: [PATCH 130/203] hid hid_get_input_report workarounds --- lib/python/xap_client/client.py | 8 +++++--- lib/python/xap_client/device.py | 4 +++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/python/xap_client/client.py b/lib/python/xap_client/client.py index 92258b7594cd..598241548f4f 100644 --- a/lib/python/xap_client/client.py +++ b/lib/python/xap_client/client.py @@ -1,13 +1,12 @@ # Copyright 2022 QMK # SPDX-License-Identifier: GPL-2.0-or-later -import hid - +from typing import List class XAPClient: """XAP device discovery """ @staticmethod - def devices(search: str = None) -> list[dict]: + def devices(search: str = None) -> List[dict]: """Find compatible XAP devices Args: @@ -20,6 +19,9 @@ def _is_filtered_device(x): name = '%04x:%04x' % (x['vendor_id'], x['product_id']) return name.lower().startswith(search.lower()) + # lazy import to avoid compile issues + import hid + devices = filter(_is_xap_usage, hid.enumerate()) if search: devices = filter(_is_filtered_device, devices) diff --git a/lib/python/xap_client/device.py b/lib/python/xap_client/device.py index bd7f5691fb95..b26276251ba4 100644 --- a/lib/python/xap_client/device.py +++ b/lib/python/xap_client/device.py @@ -1,6 +1,5 @@ # Copyright 2022 QMK # SPDX-License-Identifier: GPL-2.0-or-later -import hid import json import time import gzip @@ -40,6 +39,9 @@ def __init__(self, dev: dict, timeout: int = 1.0): self.timeout = timeout self.running = True + # lazy import to avoid compile issues + import hid + self.dev = hid.Device(path=dev['path']) self.bg = threading.Thread(target=self._read_loop, daemon=True) From 76a45a4e24361e8dfbf76489a31ec847c0f9156d Mon Sep 17 00:00:00 2001 From: zvecr Date: Mon, 26 Sep 2022 01:09:15 +0100 Subject: [PATCH 131/203] Stub out nested routes --- data/templates/xap/client/python/routes.py.j2 | 5 +++++ data/templates/xap/docs/route_request.md.j2 | 8 +++---- data/templates/xap/docs/route_response.md.j2 | 8 +++---- data/templates/xap/docs/routes.md.j2 | 22 +++++++++++++++++++ .../qmk/xap/gen_firmware/inline_generator.py | 9 ++++++++ lib/python/xap_client/device.py | 8 +++++-- 6 files changed, 50 insertions(+), 10 deletions(-) diff --git a/data/templates/xap/client/python/routes.py.j2 b/data/templates/xap/client/python/routes.py.j2 index 7eb6a5bde2e2..3cd65912e76a 100644 --- a/data/templates/xap/client/python/routes.py.j2 +++ b/data/templates/xap/client/python/routes.py.j2 @@ -9,6 +9,11 @@ class XAPRoutes(): # {{route.define}} {%- for subid, subroute in route.routes | dictsort %} {{route.define}}_{{subroute.define}} = b'\x{{ '%02d' % id|int(base=16) }}\x{{ '%02d' % subid|int(base=16) }}' +{%- if subroute.routes %} +{%- for subsubid, subsubroute in subroute.routes | dictsort %} + {{route.define}}_{{subroute.define}}_{{subsubroute.define}} = b'\x{{ '%02d' % id|int(base=16) }}\x{{ '%02d' % subid|int(base=16) }}\x{{ '%02d' % subsubid|int(base=16) }}' +{%- endfor %} +{%- endif %} {%- endfor %} {%- endif %} {%- endfor %} \ No newline at end of file diff --git a/data/templates/xap/docs/route_request.md.j2 b/data/templates/xap/docs/route_request.md.j2 index fd8b81ee1636..8d9c1fd37dbe 100644 --- a/data/templates/xap/docs/route_request.md.j2 +++ b/data/templates/xap/docs/route_request.md.j2 @@ -1,8 +1,8 @@ -{%- if subroute.request_type == 'struct' -%} +{%- if route.request_type == 'struct' -%} __Request:__ -{%- for member in subroute.request_struct_members -%} +{%- for member in route.request_struct_members -%}
{{ " "|safe*4 }}* {{ member.name }}: `{{ member.type }}` {%- endfor -%} -{%- elif subroute.request_type -%} -__Request:__ `{{ subroute.request_type }}` +{%- elif route.request_type -%} +__Request:__ `{{ route.request_type }}` {%- endif -%} \ No newline at end of file diff --git a/data/templates/xap/docs/route_response.md.j2 b/data/templates/xap/docs/route_response.md.j2 index 58af87d46757..64f4e065e08b 100644 --- a/data/templates/xap/docs/route_response.md.j2 +++ b/data/templates/xap/docs/route_response.md.j2 @@ -1,8 +1,8 @@ -{%- if subroute.return_type == 'struct' -%} +{%- if route.return_type == 'struct' -%} __Response:__ -{%- for member in subroute.return_struct_members -%} +{%- for member in route.return_struct_members -%}
{{ " "|safe*4 }}* {{ member.name }}: `{{ member.type }}` {%- endfor -%} -{%- elif subroute.return_type -%} -__Response:__ `{{ subroute.return_type }}` +{%- elif route.return_type -%} +__Response:__ `{{ route.return_type }}` {%- endif -%} \ No newline at end of file diff --git a/data/templates/xap/docs/routes.md.j2 b/data/templates/xap/docs/routes.md.j2 index 791eaac5643f..4c6edee210fc 100644 --- a/data/templates/xap/docs/routes.md.j2 +++ b/data/templates/xap/docs/routes.md.j2 @@ -6,7 +6,29 @@ | Name | Route | Tags | Payloads | Description | | -- | -- | -- | -- | -- | {%- for subid, subroute in route.routes | dictsort %} +{%- if not subroute.routes %} +{%- with route=subroute %} | {{ subroute.name }} | `{{ id }} {{ subid }}` | {% if 'secure' == subroute.permissions %}__Secure__{% endif %} | {%- include 'route_request.md.j2' -%}{%- if subroute.return_type and subroute.request_type -%}

{% endif %}{%- include 'route_response.md.j2' -%} | {{ subroute.description.replace('\n', '
') }}| +{%- endwith %} +{%- endif %} +{%- endfor %} + +{%- for subid, subroute in route.routes | dictsort %} +{%- if subroute.routes %} + +#### {{ subroute.name }} - `{{ id }} {{ subid }}` +{{ subroute.description }} + +| Name | Route | Tags | Payloads | Description | +| -- | -- | -- | -- | -- | +{%- for subsubid, subsubroute in subroute.routes | dictsort %} +{%- if not subsubroute.routes %} +{%- with route=subsubroute %} +| {{ subsubroute.name }} | `{{ id }} {{ subid }} {{ subsubid }}` | {% if 'secure' == subsubroute.permissions %}__Secure__{% endif %} | {%- include 'route_request.md.j2' -%}{%- if subsubroute.return_type and subsubroute.request_type -%}

{% endif %}{%- include 'route_response.md.j2' -%} | {{ subsubroute.description.replace('\n', '
') }}| +{%- endwith %} +{%- endif %} +{%- endfor %} +{%- endif %} {%- endfor %} {% endif %} {%- endfor %} diff --git a/lib/python/qmk/xap/gen_firmware/inline_generator.py b/lib/python/qmk/xap/gen_firmware/inline_generator.py index f8291b5e06ab..9d0a6ed54c9f 100755 --- a/lib/python/qmk/xap/gen_firmware/inline_generator.py +++ b/lib/python/qmk/xap/gen_firmware/inline_generator.py @@ -53,6 +53,8 @@ def _get_route_type(container): return 'XAP_CONST_MEM' elif container['return_type'] == 'u32': return 'XAP_CONST_MEM' + elif container['return_type'] == 'u64': + return 'XAP_CONST_MEM' elif container['return_type'] == 'struct': return 'XAP_CONST_MEM' elif container['return_type'] == 'string': @@ -98,6 +100,11 @@ def _append_routing_table_declaration(lines, container, container_id, route_stac lines.append('') lines.append(f'static const uint32_t {route_name}_data PROGMEM = {constant};') + elif container['return_type'] == 'u64': + constant = container['return_constant'] + lines.append('') + lines.append(f'static const uint64_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 = {{') @@ -196,6 +203,8 @@ def _append_routing_table_entry(lines, container, container_id, route_stack): _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'] == 'u64': + _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': diff --git a/lib/python/xap_client/device.py b/lib/python/xap_client/device.py index b26276251ba4..8196b6517e47 100644 --- a/lib/python/xap_client/device.py +++ b/lib/python/xap_client/device.py @@ -147,8 +147,12 @@ def _ensure_route(self, route: bytes): XAPRouteError: Access to invalid route attempted """ # TODO: Remove assumption that capability is always xx01 - (sub, rt) = route - cap = bytes([sub, 1]) + (remain, sub, rt) = (route[:-2], route[-2], route[-1]) + cap = remain + bytes([sub, 1]) + + # recurse for nested routes + if remain: + self._ensure_route(remain + bytes([sub])) if self.subsystems() & (1 << sub) == 0: raise XAPRouteError("subsystem not available") From 1dbb4c0f96de5b557d21b7062dbd0fedf85c48cf Mon Sep 17 00:00:00 2001 From: zvecr Date: Mon, 26 Sep 2022 18:09:36 +0100 Subject: [PATCH 132/203] stash --- data/templates/xap/client/python/types.py.j2 | 31 ++- data/xap/xap_0.2.0.hjson | 1 - data/xap/xap_0.3.0.hjson | 78 ++++++++ docs/xap_0.3.0.md | 194 +++++++++++++++++++ docs/xap_protocol.md | 1 + lib/python/qmk/cli/xap/xap.py | 10 +- lib/python/qmk/xap/common.py | 3 +- lib/python/xap_client/client.py | 1 + lib/python/xap_client/routes.py | 4 + lib/python/xap_client/types.py | 23 +++ quantum/xap/xap.c | 4 + quantum/xap/xap_handlers.c | 17 ++ 12 files changed, 359 insertions(+), 8 deletions(-) create mode 100644 data/xap/xap_0.3.0.hjson create mode 100644 docs/xap_0.3.0.md diff --git a/data/templates/xap/client/python/types.py.j2 b/data/templates/xap/client/python/types.py.j2 index 2509688abfdb..8ed0e624da94 100644 --- a/data/templates/xap/client/python/types.py.j2 +++ b/data/templates/xap/client/python/types.py.j2 @@ -2,10 +2,9 @@ from collections import namedtuple from enum import IntFlag, IntEnum from struct import Struct -{% set type_definitions = [{'name':'XAPRequest', 'members': 'token length data', 'fmt':'`token` - token
`u8` - type
`u8` - length | +| _ID_ | A single octet / 8-bit byte, representing Subsystem or Route index. | +| _Request Header_ | Packet format for inbound data. Takes the format:
`token` - token
`u8` - length | +| _Response Flags_ | An `u8` containing the status of the request. | +| _Response Header_ | Packet format for outbound data. Takes the format:
`token` - token
`response_flags` - flags
`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 | +| -- | -- | -- | -- | -- | -- | -- | -- | +| `-` | `-` | `-` | `-` | `-` | `-` | `SECURE_FAILURE` | `SUCCESS` | + +* 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.

* 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}.| +| 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

* 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| +| 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.

* 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}.| +| 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:__
    * Vendor ID: `u16`
    * Product ID: `u16`
    * Product Version: `u16`
    * 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:__ `u16`| Retrieves the length of the configuration data bundled within the firmware| +| Config Blob Chunk | `0x01 0x06` | |__Request:__ `u16`

__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

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| +| 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:__
    * Layer: `u8`
    * Row: `u8`
    * Column: `u8`

__Response:__ `u16`| Query the Keycode at the requested location.| +| Get Encoder Keycode | `0x04 0x04` | |__Request:__
    * Layer: `u8`
    * Encoder: `u8`
    * Clockwise: `u8`

__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:__
    * Layer: `u8`
    * Row: `u8`
    * Column: `u8`
    * Keycode: `u16`| Modify the Keycode at the requested location.| +| Set Encoder Keycode | `0x05 0x04` | __Secure__ |__Request:__
    * Layer: `u8`
    * Encoder: `u8`
    * Clockwise: `u8`
    * 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.| + +#### rgblight - `0x06 0x03` +This subsystem allows for control over the rgblight subsystem. + +| Name | Route | Tags | Payloads | Description | +| -- | -- | -- | -- | -- | +| Capabilities Query | `0x06 0x03 0x01` | |__Response:__ `u32`| rgblight subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.| +| Get Enabled Effects | `0x06 0x03 0x02` | |__Response:__ `u64`| Each bit should be considered as a "usable" effect id| +| Get Config | `0x06 0x03 0x03` | |__Response:__
    * enable: `u8`
    * mode: `u8`
    * hue: `u8`
    * sat: `u8`
    * val: `u8`
    * speed: `u8`| Query the current config.| + + +## 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`( ) | `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. + diff --git a/docs/xap_protocol.md b/docs/xap_protocol.md index 08390580bbe0..f70f605bb5b2 100644 --- a/docs/xap_protocol.md +++ b/docs/xap_protocol.md @@ -1,4 +1,5 @@ +* [XAP Version 0.3.0](xap_0.3.0.md) * [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) diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index c050a4f2e759..767ccf7b2516 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -7,7 +7,7 @@ from qmk.keyboard import render_layout from qmk.xap.common import get_xap_keycodes -from xap_client import XAPClient, XAPEventType, XAPSecureStatus +from xap_client import XAPClient, XAPEventType, XAPSecureStatus, XAPConfigRgblight KEYCODE_MAP = get_xap_keycodes('latest') @@ -187,6 +187,14 @@ def loop(self): print('^C') return False + def do_dump(self, line): + ret = self.device.transaction(b'\x06\x03\x03') + ret = XAPConfigRgblight.from_bytes(ret) + print(ret) + + ret = self.device.int_transaction(b'\x06\x03\x02') + print(f'XAPEffectRgblight(enabled={bin(ret)})') + @cli.argument('-v', '--verbose', arg_only=True, action='store_true', help='Turns on verbose output.') @cli.argument('-d', '--device', help='device to select - uses format :.') diff --git a/lib/python/qmk/xap/common.py b/lib/python/qmk/xap/common.py index 64372db804e0..da888b09c8c7 100755 --- a/lib/python/qmk/xap/common.py +++ b/lib/python/qmk/xap/common.py @@ -7,6 +7,7 @@ from typing import OrderedDict from jinja2 import Environment, FileSystemLoader, select_autoescape +from qmk.casing import to_snake from qmk.constants import QMK_FIRMWARE from qmk.json_schema import json_load, validate from qmk.decorators import lru_cache @@ -24,7 +25,7 @@ def _get_jinja2_env(data_templates_xap_subdir: str): 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)) + return j2.get_template(file_to_render).render(xap=defs, xap_str=hjson.dumps(defs), to_snake=to_snake) def _find_kb_spec(kb): diff --git a/lib/python/xap_client/client.py b/lib/python/xap_client/client.py index 598241548f4f..f4631b871228 100644 --- a/lib/python/xap_client/client.py +++ b/lib/python/xap_client/client.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later from typing import List + class XAPClient: """XAP device discovery """ diff --git a/lib/python/xap_client/routes.py b/lib/python/xap_client/routes.py index 13f8f3cf6f58..e5a0891bced3 100644 --- a/lib/python/xap_client/routes.py +++ b/lib/python/xap_client/routes.py @@ -61,3 +61,7 @@ class XAPRoutes(): REMAPPING_SET_ENCODER_KEYCODE = b'\x05\x04' # LIGHTING LIGHTING_CAPABILITIES_QUERY = b'\x06\x01' + LIGHTING_RGBLIGHT = b'\x06\x03' + LIGHTING_RGBLIGHT_CAPABILITIES_QUERY = b'\x06\x03\x01' + LIGHTING_RGBLIGHT_GET_ENABLED_EFFECTS = b'\x06\x03\x02' + LIGHTING_RGBLIGHT_GET_CONFIG = b'\x06\x03\x03' diff --git a/lib/python/xap_client/types.py b/lib/python/xap_client/types.py index 626830a475ce..edb19dc62a3b 100644 --- a/lib/python/xap_client/types.py +++ b/lib/python/xap_client/types.py @@ -59,6 +59,29 @@ def to_bytes(self): return self.fmt.pack(*list(self)) +class XAPConfigRgblight(namedtuple('XAPConfigRgblight', 'enable mode hue sat val speed')): + fmt = Struct(' Date: Wed, 28 Sep 2022 23:47:12 +0100 Subject: [PATCH 133/203] stash more lighting --- data/constants/led_matrix_0.0.1.json | 80 ++++++++++ data/constants/rgb_matrix_0.0.1.json | 156 +++++++++++++++++++ data/constants/rgblight_0.0.1.json | 130 ++++++++++++++++ data/templates/xap/client/python/types.py.j2 | 4 +- data/xap/xap_0.3.0.hjson | 135 ++++++++++++++++ docs/xap_0.3.0.md | 18 +++ lib/python/qmk/cli/xap/xap.py | 31 +++- lib/python/xap_client/routes.py | 8 + lib/python/xap_client/types.py | 30 ++++ quantum/xap/xap.c | 5 + quantum/xap/xap_handlers.c | 119 +++++++++++++- 11 files changed, 708 insertions(+), 8 deletions(-) create mode 100644 data/constants/led_matrix_0.0.1.json create mode 100644 data/constants/rgb_matrix_0.0.1.json create mode 100644 data/constants/rgblight_0.0.1.json diff --git a/data/constants/led_matrix_0.0.1.json b/data/constants/led_matrix_0.0.1.json new file mode 100644 index 000000000000..89373f58c2cb --- /dev/null +++ b/data/constants/led_matrix_0.0.1.json @@ -0,0 +1,80 @@ +{ + "groups": { + "reactive": {} + }, + "effects": { + "0x01": { + "key": "SOLID" + }, + "0x02": { + "key": "ALPHAS_MODS" + }, + "0x03": { + "key": "BREATHING" + }, + "0x04": { + "key": "BAND" + }, + "0x05": { + "key": "BAND_PINWHEEL" + }, + "0x06": { + "key": "BAND_SPIRAL" + }, + "0x07": { + "key": "CYCLE_LEFT_RIGHT" + }, + "0x08": { + "key": "CYCLE_UP_DOWN" + }, + "0x09": { + "key": "CYCLE_OUT_IN" + }, + "0x0A": { + "key": "DUAL_BEACON" + }, + "0x0B": { + "key": "WAVE_LEFT_RIGHT" + }, + "0x0C": { + "key": "WAVE_UP_DOWN" + }, + + "0x0D": { + "key": "SOLID_REACTIVE_SIMPLE", + "group": "reactive" + }, + "0x0E": { + "key": "SOLID_REACTIVE_WIDE", + "group": "reactive" + }, + "0x0F": { + "key": "SOLID_REACTIVE_MULTIWIDE", + "group": "reactive" + }, + "0x10": { + "key": "SOLID_REACTIVE_CROSS", + "group": "reactive" + }, + "0x11": { + "key": "SOLID_REACTIVE_MULTICROSS", + "group": "reactive" + }, + "0x12": { + "key": "SOLID_REACTIVE_NEXUS", + "group": "reactive" + }, + "0x13": { + "key": "SOLID_REACTIVE_MULTINEXUS", + "group": "reactive" + }, + "0x14": { + "key": "SOLID_SPLASH", + "group": "reactive" + }, + "0x15": { + "key": "SOLID_MULTISPLASH", + "group": "reactive" + } + } +} diff --git a/data/constants/rgb_matrix_0.0.1.json b/data/constants/rgb_matrix_0.0.1.json new file mode 100644 index 000000000000..51c1b72cb8fc --- /dev/null +++ b/data/constants/rgb_matrix_0.0.1.json @@ -0,0 +1,156 @@ +{ + "groups": { + "framebuffer": {}, + "reactive": {} + }, + "effects": { + "0x01": { + "key": "SOLID_COLOR" + }, + "0x02": { + "key": "ALPHAS_MODS" + }, + "0x03": { + "key": "GRADIENT_UP_DOWN" + }, + "0x04": { + "key": "GRADIENT_LEFT_RIGHT" + }, + "0x05": { + "key": "BREATHING" + }, + "0x06": { + "key": "BAND_SAT" + }, + "0x07": { + "key": "BAND_VAL" + }, + "0x08": { + "key": "BAND_PINWHEEL_SAT" + }, + "0x09": { + "key": "BAND_PINWHEEL_VAL" + }, + "0x0A": { + "key": "BAND_SPIRAL_SAT" + }, + "0x0B": { + "key": "BAND_SPIRAL_VAL" + }, + "0x0C": { + "key": "CYCLE_ALL" + }, + "0x0D": { + "key": "CYCLE_LEFT_RIGHT" + }, + "0x0E": { + "key": "CYCLE_UP_DOWN" + }, + "0x0F": { + "key": "CYCLE_OUT_IN" + }, + "0x10": { + "key": "CYCLE_OUT_IN_DUAL" + }, + "0x11": { + "key": "RAINBOW_MOVING_CHEVRON" + }, + "0x12": { + "key": "CYCLE_PINWHEEL" + }, + "0x13": { + "key": "CYCLE_SPIRAL" + }, + "0x14": { + "key": "DUAL_BEACON" + }, + "0x15": { + "key": "RAINBOW_BEACON" + }, + "0x16": { + "key": "RAINBOW_PINWHEELS" + }, + "0x17": { + "key": "RAINDROPS" + }, + "0x18": { + "key": "JELLYBEAN_RAINDROPS" + }, + "0x19": { + "key": "HUE_BREATHING" + }, + "0x1A": { + "key": "HUE_PENDULUM" + }, + "0x1B": { + "key": "HUE_WAVE" + }, + "0x1C": { + "key": "PIXEL_FRACTAL" + }, + "0x1D": { + "key": "PIXEL_FLOW" + }, + "0x1E": { + "key": "PIXEL_RAIN" + }, + + "0x1F": { + "key": "TYPING_HEATMAP", + "group": "framebuffer" + }, + "0x20": { + "key": "DIGITAL_RAIN", + "group": "framebuffer" + }, + + "0x21": { + "key": "SOLID_REACTIVE_SIMPLE", + "group": "reactive" + }, + "0x22": { + "key": "RGB_MATRIX_SOLID_REACTIVE", + "group": "reactive" + }, + "0x23": { + "key": "RGB_MATRIX_SOLID_REACTIVE_WIDE", + "group": "reactive" + }, + "0x24": { + "key": "SOLID_REACTIVE_MULTIWIDE", + "group": "reactive" + }, + "0x25": { + "key": "SOLID_REACTIVE_CROSS", + "group": "reactive" + }, + "0x26": { + "key": "SOLID_REACTIVE_MULTICROSS", + "group": "reactive" + }, + "0x27": { + "key": "SOLID_REACTIVE_NEXUS", + "group": "reactive" + }, + "0x28": { + "key": "SOLID_REACTIVE_MULTINEXUS", + "group": "reactive" + }, + "0x29": { + "key": "SPLASH", + "group": "reactive" + }, + "0x2A": { + "key": "MULTISPLASH", + "group": "reactive" + }, + "0x2B": { + "key": "SOLID_SPLASH", + "group": "reactive" + }, + "0x2C": { + "key": "SOLID_MULTISPLASH", + "group": "reactive" + } + } +} diff --git a/data/constants/rgblight_0.0.1.json b/data/constants/rgblight_0.0.1.json new file mode 100644 index 000000000000..20344b42d57c --- /dev/null +++ b/data/constants/rgblight_0.0.1.json @@ -0,0 +1,130 @@ +{ + "effects": { + "0x01": { + "key": "STATIC_LIGHT" + }, + "0x02": { + "key": "BREATHING" + }, + "0x03": { + "key": "BREATHING_2" + }, + "0x04": { + "key": "BREATHING_3" + }, + "0x05": { + "key": "BREATHING_4" + }, + "0x06": { + "key": "RAINBOW_MOOD" + }, + "0x07": { + "key": "RAINBOW_MOOD_2" + }, + "0x08": { + "key": "RAINBOW_MOOD_3" + }, + "0x09": { + "key": "RAINBOW_SWIRL" + }, + "0x0A": { + "key": "RAINBOW_SWIRL_2" + }, + "0x0B": { + "key": "RAINBOW_SWIRL_3" + }, + "0x0C": { + "key": "RAINBOW_SWIRL_4" + }, + "0x0D": { + "key": "RAINBOW_SWIRL_5" + }, + "0x0E": { + "key": "RAINBOW_SWIRL_6" + }, + "0x0F": { + "key": "SNAKE" + }, + "0x10": { + "key": "SNAKE_2" + }, + "0x11": { + "key": "SNAKE_3" + }, + "0x12": { + "key": "SNAKE_4" + }, + "0x13": { + "key": "SNAKE_5" + }, + "0x14": { + "key": "SNAKE_6" + }, + "0x15": { + "key": "KNIGHT" + }, + "0x16": { + "key": "KNIGHT_2" + }, + "0x17": { + "key": "KNIGHT_3" + }, + "0x18": { + "key": "CHRISTMAS" + }, + "0x19": { + "key": "STATIC_GRADIENT" + }, + "0x1A": { + "key": "STATIC_GRADIENT_2" + }, + "0x1B": { + "key": "STATIC_GRADIENT_3" + }, + "0x1C": { + "key": "STATIC_GRADIENT_4" + }, + "0x1D": { + "key": "STATIC_GRADIENT_5" + }, + "0x1E": { + "key": "STATIC_GRADIENT_6" + }, + "0x1F": { + "key": "STATIC_GRADIENT_7" + }, + "0x20": { + "key": "STATIC_GRADIENT_8" + }, + "0x21": { + "key": "STATIC_GRADIENT_9" + }, + "0x22": { + "key": "STATIC_GRADIENT_10" + }, + "0x23": { + "key": "TEST" + }, + "0x24": { + "key": "ALTERNATING" + }, + "0x25": { + "key": "TWINKLE" + }, + "0x26": { + "key": "TWINKLE_2" + }, + "0x27": { + "key": "TWINKLE_3" + }, + "0x28": { + "key": "TWINKLE_4" + }, + "0x29": { + "key": "TWINKLE_5" + }, + "0x2A": { + "key": "TWINKLE_6" + } + } +} diff --git a/data/templates/xap/client/python/types.py.j2 b/data/templates/xap/client/python/types.py.j2 index 8ed0e624da94..808f25bef260 100644 --- a/data/templates/xap/client/python/types.py.j2 +++ b/data/templates/xap/client/python/types.py.j2 @@ -22,7 +22,9 @@ class {{ name }}(namedtuple('{{ name }}', '{{ members }}')): {% set type_definitions = [ {'name':'XAPRequest', 'members': 'token length data', 'fmt':'    * enable: `u8`
    * mode: `u8`
    * val: `u8`| Query the current config.| + #### rgblight - `0x06 0x03` This subsystem allows for control over the rgblight subsystem. @@ -161,6 +170,15 @@ This subsystem allows for control over the rgblight subsystem. | Get Enabled Effects | `0x06 0x03 0x02` | |__Response:__ `u64`| Each bit should be considered as a "usable" effect id| | Get Config | `0x06 0x03 0x03` | |__Response:__
    * enable: `u8`
    * mode: `u8`
    * hue: `u8`
    * sat: `u8`
    * val: `u8`
    * speed: `u8`| Query the current config.| +#### rgbmatrix - `0x06 0x04` +This subsystem allows for control over the rgb matrix subsystem. + +| Name | Route | Tags | Payloads | Description | +| -- | -- | -- | -- | -- | +| Capabilities Query | `0x06 0x04 0x01` | |__Response:__ `u32`| rgb matrix subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.| +| Get Enabled Effects | `0x06 0x04 0x02` | |__Response:__ `u64`| Each bit should be considered as a "usable" effect id| +| Get Config | `0x06 0x04 0x03` | |__Response:__
    * enable: `u8`
    * mode: `u8`
    * hue: `u8`
    * sat: `u8`
    * val: `u8`
    * speed: `u8`
    * flags: `u8`| Query the current config.| + ## Broadcast messages diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index 767ccf7b2516..f1f28f86ce02 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -7,7 +7,7 @@ from qmk.keyboard import render_layout from qmk.xap.common import get_xap_keycodes -from xap_client import XAPClient, XAPEventType, XAPSecureStatus, XAPConfigRgblight +from xap_client import XAPClient, XAPEventType, XAPSecureStatus, XAPConfigRgblight, XAPConfigBacklight, XAPConfigRgbMatrix, XAPRoutes KEYCODE_MAP = get_xap_keycodes('latest') @@ -188,12 +188,31 @@ def loop(self): return False def do_dump(self, line): - ret = self.device.transaction(b'\x06\x03\x03') - ret = XAPConfigRgblight.from_bytes(ret) - print(ret) + caps = self.device.int_transaction(XAPRoutes.LIGHTING_CAPABILITIES_QUERY) - ret = self.device.int_transaction(b'\x06\x03\x02') - print(f'XAPEffectRgblight(enabled={bin(ret)})') + if caps & (1 << XAPRoutes.LIGHTING_BACKLIGHT[-1]): + ret = self.device.transaction(XAPRoutes.LIGHTING_BACKLIGHT_GET_CONFIG) + ret = XAPConfigBacklight.from_bytes(ret) + print(ret) + + ret = self.device.int_transaction(XAPRoutes.LIGHTING_BACKLIGHT_GET_ENABLED_EFFECTS) + print(f'XAPEffectBacklight(enabled={bin(ret)})') + + if caps & (1 << XAPRoutes.LIGHTING_RGBLIGHT[-1]): + ret = self.device.transaction(XAPRoutes.LIGHTING_RGBLIGHT_GET_CONFIG) + ret = XAPConfigRgblight.from_bytes(ret) + print(ret) + + ret = self.device.int_transaction(XAPRoutes.LIGHTING_RGBLIGHT_GET_ENABLED_EFFECTS) + print(f'XAPEffectRgblight(enabled={bin(ret)})') + + if caps & (1 << XAPRoutes.LIGHTING_RGB_MATRIX[-1]): + ret = self.device.transaction(XAPRoutes.LIGHTING_RGB_MATRIX_GET_CONFIG) + ret = XAPConfigRgbMatrix.from_bytes(ret) + print(ret) + + ret = self.device.int_transaction(XAPRoutes.LIGHTING_RGB_MATRIX_GET_ENABLED_EFFECTS) + print(f'XAPEffectRgbMatrix(enabled={bin(ret)})') @cli.argument('-v', '--verbose', arg_only=True, action='store_true', help='Turns on verbose output.') diff --git a/lib/python/xap_client/routes.py b/lib/python/xap_client/routes.py index e5a0891bced3..c8ebb415f894 100644 --- a/lib/python/xap_client/routes.py +++ b/lib/python/xap_client/routes.py @@ -61,7 +61,15 @@ class XAPRoutes(): REMAPPING_SET_ENCODER_KEYCODE = b'\x05\x04' # LIGHTING LIGHTING_CAPABILITIES_QUERY = b'\x06\x01' + LIGHTING_BACKLIGHT = b'\x06\x02' + LIGHTING_BACKLIGHT_CAPABILITIES_QUERY = b'\x06\x02\x01' + LIGHTING_BACKLIGHT_GET_ENABLED_EFFECTS = b'\x06\x02\x02' + LIGHTING_BACKLIGHT_GET_CONFIG = b'\x06\x02\x03' LIGHTING_RGBLIGHT = b'\x06\x03' LIGHTING_RGBLIGHT_CAPABILITIES_QUERY = b'\x06\x03\x01' LIGHTING_RGBLIGHT_GET_ENABLED_EFFECTS = b'\x06\x03\x02' LIGHTING_RGBLIGHT_GET_CONFIG = b'\x06\x03\x03' + LIGHTING_RGB_MATRIX = b'\x06\x04' + LIGHTING_RGB_MATRIX_CAPABILITIES_QUERY = b'\x06\x04\x01' + LIGHTING_RGB_MATRIX_GET_ENABLED_EFFECTS = b'\x06\x04\x02' + LIGHTING_RGB_MATRIX_GET_CONFIG = b'\x06\x04\x03' diff --git a/lib/python/xap_client/types.py b/lib/python/xap_client/types.py index edb19dc62a3b..60416795ce26 100644 --- a/lib/python/xap_client/types.py +++ b/lib/python/xap_client/types.py @@ -59,6 +59,20 @@ def to_bytes(self): return self.fmt.pack(*list(self)) +class XAPConfigBacklight(namedtuple('XAPConfigBacklight', 'enable mode val')): + fmt = Struct(' Date: Thu, 29 Sep 2022 23:44:11 +0100 Subject: [PATCH 134/203] Finish stub for effect mapping --- data/constants/led_matrix_0.0.1.json | 42 +++---- data/constants/rgb_matrix_0.0.1.json | 88 ++++++------- data/constants/rgblight_0.0.1.json | 86 ++++++------- lib/python/qmk/cli/xap/generate_qmk.py | 12 +- .../qmk/xap/gen_firmware/inline_generator.py | 116 ++++++++++++++++++ quantum/xap/xap.c | 4 +- quantum/xap/xap_handlers.c | 84 +------------ 7 files changed, 237 insertions(+), 195 deletions(-) diff --git a/data/constants/led_matrix_0.0.1.json b/data/constants/led_matrix_0.0.1.json index 89373f58c2cb..5d59c7a14ce2 100644 --- a/data/constants/led_matrix_0.0.1.json +++ b/data/constants/led_matrix_0.0.1.json @@ -3,76 +3,76 @@ "reactive": {} }, "effects": { - "0x01": { + "0x00": { "key": "SOLID" }, - "0x02": { + "0x01": { "key": "ALPHAS_MODS" }, - "0x03": { + "0x02": { "key": "BREATHING" }, - "0x04": { + "0x03": { "key": "BAND" }, - "0x05": { + "0x04": { "key": "BAND_PINWHEEL" }, - "0x06": { + "0x05": { "key": "BAND_SPIRAL" }, - "0x07": { + "0x06": { "key": "CYCLE_LEFT_RIGHT" }, - "0x08": { + "0x07": { "key": "CYCLE_UP_DOWN" }, - "0x09": { + "0x08": { "key": "CYCLE_OUT_IN" }, - "0x0A": { + "0x09": { "key": "DUAL_BEACON" }, - "0x0B": { + "0x0A": { "key": "WAVE_LEFT_RIGHT" }, - "0x0C": { + "0x0B": { "key": "WAVE_UP_DOWN" }, - "0x0D": { + "0x0C": { "key": "SOLID_REACTIVE_SIMPLE", "group": "reactive" }, - "0x0E": { + "0x0D": { "key": "SOLID_REACTIVE_WIDE", "group": "reactive" }, - "0x0F": { + "0x0E": { "key": "SOLID_REACTIVE_MULTIWIDE", "group": "reactive" }, - "0x10": { + "0x0F": { "key": "SOLID_REACTIVE_CROSS", "group": "reactive" }, - "0x11": { + "0x10": { "key": "SOLID_REACTIVE_MULTICROSS", "group": "reactive" }, - "0x12": { + "0x11": { "key": "SOLID_REACTIVE_NEXUS", "group": "reactive" }, - "0x13": { + "0x12": { "key": "SOLID_REACTIVE_MULTINEXUS", "group": "reactive" }, - "0x14": { + "0x13": { "key": "SOLID_SPLASH", "group": "reactive" }, - "0x15": { + "0x14": { "key": "SOLID_MULTISPLASH", "group": "reactive" } diff --git a/data/constants/rgb_matrix_0.0.1.json b/data/constants/rgb_matrix_0.0.1.json index 51c1b72cb8fc..350dcdeb0745 100644 --- a/data/constants/rgb_matrix_0.0.1.json +++ b/data/constants/rgb_matrix_0.0.1.json @@ -4,151 +4,151 @@ "reactive": {} }, "effects": { - "0x01": { + "0x00": { "key": "SOLID_COLOR" }, - "0x02": { + "0x01": { "key": "ALPHAS_MODS" }, - "0x03": { + "0x02": { "key": "GRADIENT_UP_DOWN" }, - "0x04": { + "0x03": { "key": "GRADIENT_LEFT_RIGHT" }, - "0x05": { + "0x04": { "key": "BREATHING" }, - "0x06": { + "0x05": { "key": "BAND_SAT" }, - "0x07": { + "0x06": { "key": "BAND_VAL" }, - "0x08": { + "0x07": { "key": "BAND_PINWHEEL_SAT" }, - "0x09": { + "0x08": { "key": "BAND_PINWHEEL_VAL" }, - "0x0A": { + "0x09": { "key": "BAND_SPIRAL_SAT" }, - "0x0B": { + "0x0A": { "key": "BAND_SPIRAL_VAL" }, - "0x0C": { + "0x0B": { "key": "CYCLE_ALL" }, - "0x0D": { + "0x0C": { "key": "CYCLE_LEFT_RIGHT" }, - "0x0E": { + "0x0D": { "key": "CYCLE_UP_DOWN" }, - "0x0F": { + "0x0E": { "key": "CYCLE_OUT_IN" }, - "0x10": { + "0x0F": { "key": "CYCLE_OUT_IN_DUAL" }, - "0x11": { + "0x10": { "key": "RAINBOW_MOVING_CHEVRON" }, - "0x12": { + "0x11": { "key": "CYCLE_PINWHEEL" }, - "0x13": { + "0x12": { "key": "CYCLE_SPIRAL" }, - "0x14": { + "0x13": { "key": "DUAL_BEACON" }, - "0x15": { + "0x14": { "key": "RAINBOW_BEACON" }, - "0x16": { + "0x15": { "key": "RAINBOW_PINWHEELS" }, - "0x17": { + "0x16": { "key": "RAINDROPS" }, - "0x18": { + "0x17": { "key": "JELLYBEAN_RAINDROPS" }, - "0x19": { + "0x18": { "key": "HUE_BREATHING" }, - "0x1A": { + "0x19": { "key": "HUE_PENDULUM" }, - "0x1B": { + "0x1A": { "key": "HUE_WAVE" }, - "0x1C": { + "0x1B": { "key": "PIXEL_FRACTAL" }, - "0x1D": { + "0x1C": { "key": "PIXEL_FLOW" }, - "0x1E": { + "0x1D": { "key": "PIXEL_RAIN" }, - "0x1F": { + "0x1E": { "key": "TYPING_HEATMAP", "group": "framebuffer" }, - "0x20": { + "0x1F": { "key": "DIGITAL_RAIN", "group": "framebuffer" }, - "0x21": { + "0x20": { "key": "SOLID_REACTIVE_SIMPLE", "group": "reactive" }, - "0x22": { + "0x21": { "key": "RGB_MATRIX_SOLID_REACTIVE", "group": "reactive" }, - "0x23": { + "0x22": { "key": "RGB_MATRIX_SOLID_REACTIVE_WIDE", "group": "reactive" }, - "0x24": { + "0x23": { "key": "SOLID_REACTIVE_MULTIWIDE", "group": "reactive" }, - "0x25": { + "0x24": { "key": "SOLID_REACTIVE_CROSS", "group": "reactive" }, - "0x26": { + "0x25": { "key": "SOLID_REACTIVE_MULTICROSS", "group": "reactive" }, - "0x27": { + "0x26": { "key": "SOLID_REACTIVE_NEXUS", "group": "reactive" }, - "0x28": { + "0x27": { "key": "SOLID_REACTIVE_MULTINEXUS", "group": "reactive" }, - "0x29": { + "0x28": { "key": "SPLASH", "group": "reactive" }, - "0x2A": { + "0x29": { "key": "MULTISPLASH", "group": "reactive" }, - "0x2B": { + "0x2A": { "key": "SOLID_SPLASH", "group": "reactive" }, - "0x2C": { + "0x2B": { "key": "SOLID_MULTISPLASH", "group": "reactive" } diff --git a/data/constants/rgblight_0.0.1.json b/data/constants/rgblight_0.0.1.json index 20344b42d57c..e096628ae4da 100644 --- a/data/constants/rgblight_0.0.1.json +++ b/data/constants/rgblight_0.0.1.json @@ -1,129 +1,129 @@ { "effects": { - "0x01": { + "0x00": { "key": "STATIC_LIGHT" }, - "0x02": { + "0x01": { "key": "BREATHING" }, - "0x03": { + "0x02": { "key": "BREATHING_2" }, - "0x04": { + "0x03": { "key": "BREATHING_3" }, - "0x05": { + "0x04": { "key": "BREATHING_4" }, - "0x06": { + "0x05": { "key": "RAINBOW_MOOD" }, - "0x07": { + "0x06": { "key": "RAINBOW_MOOD_2" }, - "0x08": { + "0x07": { "key": "RAINBOW_MOOD_3" }, - "0x09": { + "0x08": { "key": "RAINBOW_SWIRL" }, - "0x0A": { + "0x09": { "key": "RAINBOW_SWIRL_2" }, - "0x0B": { + "0x0A": { "key": "RAINBOW_SWIRL_3" }, - "0x0C": { + "0x0B": { "key": "RAINBOW_SWIRL_4" }, - "0x0D": { + "0x0C": { "key": "RAINBOW_SWIRL_5" }, - "0x0E": { + "0x0D": { "key": "RAINBOW_SWIRL_6" }, - "0x0F": { + "0x0E": { "key": "SNAKE" }, - "0x10": { + "0x0F": { "key": "SNAKE_2" }, - "0x11": { + "0x10": { "key": "SNAKE_3" }, - "0x12": { + "0x11": { "key": "SNAKE_4" }, - "0x13": { + "0x12": { "key": "SNAKE_5" }, - "0x14": { + "0x13": { "key": "SNAKE_6" }, - "0x15": { + "0x14": { "key": "KNIGHT" }, - "0x16": { + "0x15": { "key": "KNIGHT_2" }, - "0x17": { + "0x16": { "key": "KNIGHT_3" }, - "0x18": { + "0x17": { "key": "CHRISTMAS" }, - "0x19": { + "0x18": { "key": "STATIC_GRADIENT" }, - "0x1A": { + "0x19": { "key": "STATIC_GRADIENT_2" }, - "0x1B": { + "0x1A": { "key": "STATIC_GRADIENT_3" }, - "0x1C": { + "0x1B": { "key": "STATIC_GRADIENT_4" }, - "0x1D": { + "0x1C": { "key": "STATIC_GRADIENT_5" }, - "0x1E": { + "0x1D": { "key": "STATIC_GRADIENT_6" }, - "0x1F": { + "0x1E": { "key": "STATIC_GRADIENT_7" }, - "0x20": { + "0x1F": { "key": "STATIC_GRADIENT_8" }, - "0x21": { + "0x20": { "key": "STATIC_GRADIENT_9" }, - "0x22": { + "0x21": { "key": "STATIC_GRADIENT_10" }, - "0x23": { - "key": "TEST" + "0x22": { + "key": "RGB_TEST" }, - "0x24": { + "0x23": { "key": "ALTERNATING" }, - "0x25": { + "0x24": { "key": "TWINKLE" }, - "0x26": { + "0x25": { "key": "TWINKLE_2" }, - "0x27": { + "0x26": { "key": "TWINKLE_3" }, - "0x28": { + "0x27": { "key": "TWINKLE_4" }, - "0x29": { + "0x28": { "key": "TWINKLE_5" }, - "0x2A": { + "0x29": { "key": "TWINKLE_6" } } diff --git a/lib/python/qmk/cli/xap/generate_qmk.py b/lib/python/qmk/cli/xap/generate_qmk.py index 5144d5fd7224..5339166b4b18 100755 --- a/lib/python/qmk/cli/xap/generate_qmk.py +++ b/lib/python/qmk/cli/xap/generate_qmk.py @@ -19,11 +19,11 @@ def xap_generate_qmk_inc(cli): # Determine our keyboard/keymap if not cli.args.keyboard: cli.log.error('Missing parameter: --keyboard') - cli.subcommands['xap-generate-info-h'].print_help() + cli.subcommands['xap-generate-qmk-inc'].print_help() return False if not cli.args.keymap: cli.log.error('Missing parameter: --keymap') - cli.subcommands['xap-generate-info-h'].print_help() + cli.subcommands['xap-generate-qmk-inc'].print_help() return False generate_inline(cli.args.output, cli.args.keyboard, cli.args.keymap) @@ -39,11 +39,11 @@ def xap_generate_qmk_h(cli): # Determine our keyboard/keymap if not cli.args.keyboard: cli.log.error('Missing parameter: --keyboard') - cli.subcommands['xap-generate-info-h'].print_help() + cli.subcommands['xap-generate-qmk-h'].print_help() return False if not cli.args.keymap: cli.log.error('Missing parameter: --keymap') - cli.subcommands['xap-generate-info-h'].print_help() + cli.subcommands['xap-generate-qmk-h'].print_help() return False generate_header(cli.args.output, cli.args.keyboard, cli.args.keymap) @@ -59,11 +59,11 @@ def xap_generate_qmk_blob_h(cli): # Determine our keyboard/keymap if not cli.args.keyboard: cli.log.error('Missing parameter: --keyboard') - cli.subcommands['xap-generate-info-h'].print_help() + cli.subcommands['xap-generate-qmk-blob-h'].print_help() return False if not cli.args.keymap: cli.log.error('Missing parameter: --keymap') - cli.subcommands['xap-generate-info-h'].print_help() + cli.subcommands['xap-generate-qmk-blob-h'].print_help() return False generate_blob(cli.args.output, cli.args.keyboard, cli.args.keymap) diff --git a/lib/python/qmk/xap/gen_firmware/inline_generator.py b/lib/python/qmk/xap/gen_firmware/inline_generator.py index 9d0a6ed54c9f..e6fe3f60223e 100755 --- a/lib/python/qmk/xap/gen_firmware/inline_generator.py +++ b/lib/python/qmk/xap/gen_firmware/inline_generator.py @@ -1,9 +1,46 @@ """This script generates the XAP protocol generated header to be compiled into QMK. """ +import re +from pathlib import Path + 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 +from qmk.json_schema import json_load + +PREFIX_MAP = { + 'rgblight': { + 'ifdef': 'RGBLIGHT_EFFECT', + 'def': 'RGBLIGHT_MODE', + }, + 'rgb_matrix': { + 'ifdef': 'ENABLE_RGB_MATRIX', + 'def': 'RGB_MATRIX', + }, + 'led_matrix': { + 'ifdef': 'ENABLE_LED_MATRIX', + 'def': 'LED_MATRIX', + }, +} + + +def _get_lighting_spec(xap_defs, feature): + version = xap_defs["uses"][feature] + spec = json_load(Path(f'data/constants/{feature}_{version}.json')) + + # preprocess for gross rgblight "mode + n" + for obj in spec.get('effects', {}).values(): + define = obj["key"] + offset = 0 + found = re.match("(.*)_(\\d+)$", define) + if found: + define = found.group(1) + offset = int(found.group(2)) - 1 + obj["define"] = define + obj["offset"] = offset + + return spec def _get_c_type(xap_type): @@ -276,6 +313,82 @@ def _append_broadcast_messages(lines, container): lines.append(f'void {name}(const void *data, size_t length){{ xap_broadcast({key}, data, length); }}') +def _append_lighting_map(lines, feature, spec): + """TODO: + """ + ifdef_prefix = PREFIX_MAP[feature]['ifdef'] + def_prefix = PREFIX_MAP[feature]['def'] + + lines.append(f'static uint8_t {feature}_effect_map[][2] = {{') + for id, obj in spec.get('effects', {}).items(): + define = obj["define"] + offset = f' + {obj["offset"]}' if obj["offset"] else '' + + lines.append(f''' +#ifdef {ifdef_prefix}_{define} + {{ {id}, {def_prefix}_{define}{offset}}}, +#endif''') + + lines.append('};') + + # add helper funcs + lines.append( + f''' +uint8_t {feature}2xap(uint8_t val) {{ + for(uint8_t i = 0; i < ARRAY_SIZE({feature}_effect_map); i++) {{ + if ({feature}_effect_map[i][1] == val) + return {feature}_effect_map[i][0]; + }} + return 0xFF; +}} + +uint8_t xap2{feature}(uint8_t val) {{ + for(uint8_t i = 0; i < ARRAY_SIZE({feature}_effect_map); i++) {{ + if ({feature}_effect_map[i][0] == val) + return {feature}_effect_map[i][1]; + }} + return 0xFF; +}}''' + ) + + +def _append_lighting_bitmask(lines, feature, spec): + """TODO: + """ + ifdef_prefix = PREFIX_MAP[feature]['ifdef'] + + lines.append(f"static const uint64_t ENABLED_{feature.upper()}_EFFECTS = 0") + for id, obj in spec.get('effects', {}).items(): + define = obj["define"] + + lines.append(f''' +#ifdef {ifdef_prefix}_{define} + | (1ULL << {id}) +#endif''') + lines.append(';') + + +def _append_lighting_mapping(lines, xap_defs): + """TODO: + """ + # TODO: remove bodge for always enabled effects + lines.append(''' +#define RGBLIGHT_EFFECT_STATIC_LIGHT +#define ENABLE_RGB_MATRIX_SOLID_COLOR +#define ENABLE_LED_MATRIX_SOLID +''') + + for feature in PREFIX_MAP.keys(): + spec = _get_lighting_spec(xap_defs, feature) + + lines.append(f"#ifdef {feature.upper()}_ENABLE") + _append_lighting_map(lines, feature, spec) + lines.append(f"#endif //{feature.upper()}_ENABLE") + + # TODO: should be inside ifdef but causes build issues + _append_lighting_bitmask(lines, feature, spec) + + def generate_inline(output_file, keyboard, keymap): """Generates the XAP protocol header file, generated during normal build. """ @@ -284,6 +397,9 @@ def generate_inline(output_file, keyboard, keymap): # Preamble lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, ''] + # TODO: gen somewhere else? + _append_lighting_mapping(lines, xap_defs) + # Add all the generated code _append_broadcast_messages(lines, xap_defs) _append_routing_tables(lines, xap_defs) diff --git a/quantum/xap/xap.c b/quantum/xap/xap.c index c9d80ba10b10..62e4590ac265 100644 --- a/quantum/xap/xap.c +++ b/quantum/xap/xap.c @@ -28,9 +28,7 @@ bool get_config_blob_chunk(uint16_t offset, uint8_t *data, uint8_t data_len) { return true; } -// TODO: return actual values -#define ENABLED_RGBLIGHT_EFFECTS 0xFFFFFFFF -#define ENABLED_RGB_MATRIX_EFFECTS 0xFFFFFFFF +// TODO: move to better location? #ifdef BACKLIGHT_BREATHING # define ENABLED_BACKLIGHT_EFFECTS 0b00000001 #else diff --git a/quantum/xap/xap_handlers.c b/quantum/xap/xap_handlers.c index ca39f7d89429..c729949ff5ea 100644 --- a/quantum/xap/xap_handlers.c +++ b/quantum/xap/xap_handlers.c @@ -182,6 +182,8 @@ bool xap_respond_dynamic_encoder_set_keycode(xap_token_t token, const void *data } #endif +#define INVALID_EFFECT 0xFF + #if ((defined(BACKLIGHT_ENABLE))) # include "backlight.h" @@ -204,66 +206,13 @@ bool xap_respond_get_backlight_config(xap_token_t token, const void *data, size_ extern rgblight_config_t rgblight_config; -// static uint8_t effect_map[][2] = { -// {1, RGBLIGHT_MODE_STATIC_LIGHT}, -// # ifdef RGBLIGHT_EFFECT_BREATHING -// {2, RGBLIGHT_MODE_BREATHING}, -// {3, RGBLIGHT_MODE_BREATHING + 1}, -// {4, RGBLIGHT_MODE_BREATHING + 2}, -// {5, RGBLIGHT_MODE_BREATHING + 3}, -// # endif -// /*...*/ -// }; - -// static uint8_t xap2rgb(uint8_t val) { -// for(uint8_t i = 0; i < ARRAY_SIZE(effect_map); i++){ -// if (effect_map[i][0] == val) { -// return effect_map[i][1]; -// } -// } -// return 0; -// } - -// static uint8_t rgb2xap(uint8_t val) { -// for(uint8_t i = 0; i < ARRAY_SIZE(effect_map); i++){ -// if (effect_map[i][1] == val) { -// return effect_map[i][0]; -// } -// } -// return 0; -// } - -static uint8_t effect_map[43] = { - 0, // - RGBLIGHT_MODE_STATIC_LIGHT, // -# ifdef RGBLIGHT_EFFECT_BREATHING - RGBLIGHT_MODE_BREATHING, - RGBLIGHT_MODE_BREATHING + 1, - RGBLIGHT_MODE_BREATHING + 2, - RGBLIGHT_MODE_BREATHING + 3, -# endif - 0, - /*...*/ -}; - -// static uint8_t xap2rgb(uint8_t val) { -// return effect_map[val]; -// } - -static uint8_t rgb2xap(uint8_t val) { - for (uint8_t i = 0; i < ARRAY_SIZE(effect_map); i++) { - if (effect_map[i] == val) { - return i; - } - } - return 0; -} +uint8_t rgblight2xap(uint8_t val); bool xap_respond_get_rgblight_config(xap_token_t token, const void *data, size_t length) { xap_route_lighting_rgblight_get_config_t ret; ret.enable = rgblight_config.enable; - ret.mode = rgb2xap(rgblight_config.mode); + ret.mode = rgblight2xap(rgblight_config.mode); ret.hue = rgblight_config.hue; ret.sat = rgblight_config.sat; ret.val = rgblight_config.val; @@ -278,34 +227,13 @@ bool xap_respond_get_rgblight_config(xap_token_t token, const void *data, size_t extern rgb_config_t rgb_matrix_config; -static uint8_t effect_map[43] = { - 0, // - RGB_MATRIX_SOLID_COLOR, // -# ifdef ENABLE_RGB_MATRIX_ALPHAS_MODS - RGB_MATRIX_ALPHAS_MODS, -# endif - 0, - /*...*/ -}; - -// static uint8_t xap2rgb(uint8_t val) { -// return effect_map[val]; -// } - -static uint8_t rgb2xap(uint8_t val) { - for (uint8_t i = 0; i < ARRAY_SIZE(effect_map); i++) { - if (effect_map[i] == val) { - return i; - } - } - return 0; -} +uint8_t rgb_matrix2xap(uint8_t val); bool xap_respond_get_rgb_matrix_config(xap_token_t token, const void *data, size_t length) { xap_route_lighting_rgb_matrix_get_config_t ret; ret.enable = rgb_matrix_config.enable; - ret.mode = rgb2xap(rgb_matrix_config.mode); + ret.mode = rgb_matrix2xap(rgb_matrix_config.mode); ret.hue = rgb_matrix_config.hsv.h; ret.sat = rgb_matrix_config.hsv.s; ret.val = rgb_matrix_config.hsv.v; From 95185b5fcc49af6fdbaf2b6d00202f85cd8785a7 Mon Sep 17 00:00:00 2001 From: zvecr Date: Fri, 30 Sep 2022 00:08:14 +0100 Subject: [PATCH 135/203] Fix dynamic keymap hash --- quantum/dynamic_keymap.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/quantum/dynamic_keymap.c b/quantum/dynamic_keymap.c index 3024fab064bb..ce201a81f04a 100644 --- a/quantum/dynamic_keymap.c +++ b/quantum/dynamic_keymap.c @@ -159,16 +159,16 @@ static uint32_t dynamic_keymap_compute_hash(void) { for (int layer = 0; layer < DYNAMIC_KEYMAP_LAYER_COUNT; layer++) { for (int row = 0; row < MATRIX_ROWS; row++) { for (int column = 0; column < MATRIX_COLS; column++) { - keycode = pgm_read_word(&keymaps[layer][row][column]); + keycode = keycode_at_keymap_location_raw(layer, row, column); hash = fnv_32a_buf(&keycode, sizeof(keycode), hash); } } # ifdef ENCODER_MAP_ENABLE for (int encoder = 0; encoder < NUM_ENCODERS; encoder++) { - keycode = pgm_read_word(&encoder_map[layer][encoder][0]); + keycode = keycode_at_encodermap_location_raw(layer, encoder, true); hash = fnv_32a_buf(&keycode, sizeof(keycode), hash); - keycode = pgm_read_word(&encoder_map[layer][encoder][1]); + keycode = keycode_at_encodermap_location_raw(layer, encoder, false); hash = fnv_32a_buf(&keycode, sizeof(keycode), hash); } # endif // ENCODER_MAP_ENABLE From 179049dfc9310a46d2d3210334e0a62c21b1d3d7 Mon Sep 17 00:00:00 2001 From: zvecr Date: Fri, 30 Sep 2022 00:16:11 +0100 Subject: [PATCH 136/203] Fix some matrix effects --- data/constants/led_matrix_0.0.1.json | 4 ++- data/constants/rgb_matrix_0.0.1.json | 12 +++++--- .../qmk/xap/gen_firmware/inline_generator.py | 29 ++++++++++++++++--- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/data/constants/led_matrix_0.0.1.json b/data/constants/led_matrix_0.0.1.json index 5d59c7a14ce2..769f3d3fbad7 100644 --- a/data/constants/led_matrix_0.0.1.json +++ b/data/constants/led_matrix_0.0.1.json @@ -1,6 +1,8 @@ { "groups": { - "reactive": {} + "reactive": { + "define": "LED_MATRIX_KEYREACTIVE_ENABLED" + } }, "effects": { "0x00": { diff --git a/data/constants/rgb_matrix_0.0.1.json b/data/constants/rgb_matrix_0.0.1.json index 350dcdeb0745..114126841a08 100644 --- a/data/constants/rgb_matrix_0.0.1.json +++ b/data/constants/rgb_matrix_0.0.1.json @@ -1,7 +1,11 @@ { "groups": { - "framebuffer": {}, - "reactive": {} + "framebuffer": { + "define": "RGB_MATRIX_FRAMEBUFFER_EFFECTS" + }, + "reactive": { + "define": "RGB_MATRIX_KEYREACTIVE_ENABLED" + } }, "effects": { "0x00": { @@ -109,11 +113,11 @@ "group": "reactive" }, "0x21": { - "key": "RGB_MATRIX_SOLID_REACTIVE", + "key": "SOLID_REACTIVE", "group": "reactive" }, "0x22": { - "key": "RGB_MATRIX_SOLID_REACTIVE_WIDE", + "key": "SOLID_REACTIVE_WIDE", "group": "reactive" }, "0x23": { diff --git a/lib/python/qmk/xap/gen_firmware/inline_generator.py b/lib/python/qmk/xap/gen_firmware/inline_generator.py index e6fe3f60223e..c987fdda1609 100755 --- a/lib/python/qmk/xap/gen_firmware/inline_generator.py +++ b/lib/python/qmk/xap/gen_firmware/inline_generator.py @@ -316,6 +316,7 @@ def _append_broadcast_messages(lines, container): def _append_lighting_map(lines, feature, spec): """TODO: """ + groups = spec.get('groups', {}) ifdef_prefix = PREFIX_MAP[feature]['ifdef'] def_prefix = PREFIX_MAP[feature]['def'] @@ -324,10 +325,19 @@ def _append_lighting_map(lines, feature, spec): define = obj["define"] offset = f' + {obj["offset"]}' if obj["offset"] else '' - lines.append(f''' + line = f''' #ifdef {ifdef_prefix}_{define} {{ {id}, {def_prefix}_{define}{offset}}}, -#endif''') +#endif''' + + group = groups.get(obj.get("group", None), {}).get('define', None) + if group: + line = f''' +#ifdef {group} +{line} +#endif''' + + lines.append(line) lines.append('};') @@ -355,16 +365,27 @@ def _append_lighting_map(lines, feature, spec): def _append_lighting_bitmask(lines, feature, spec): """TODO: """ + groups = spec.get('groups', {}) ifdef_prefix = PREFIX_MAP[feature]['ifdef'] lines.append(f"static const uint64_t ENABLED_{feature.upper()}_EFFECTS = 0") for id, obj in spec.get('effects', {}).items(): define = obj["define"] - lines.append(f''' + line = f''' #ifdef {ifdef_prefix}_{define} | (1ULL << {id}) -#endif''') +#endif''' + + group = groups.get(obj.get("group", None), {}).get('define', None) + if group: + line = f''' +#ifdef {group} +{line} +#endif''' + + lines.append(line) + lines.append(';') From 7ee53474858ff6b8fd3661615348ac44b333e0da Mon Sep 17 00:00:00 2001 From: zvecr Date: Sat, 1 Oct 2022 04:16:15 +0100 Subject: [PATCH 137/203] Add eeprom init route --- data/xap/xap_0.1.0.hjson | 20 +++++++++++++++++++- docs/xap_0.1.0.md | 3 ++- docs/xap_0.2.0.md | 3 ++- docs/xap_0.3.0.md | 3 ++- lib/python/xap_client/device.py | 6 ++++++ lib/python/xap_client/routes.py | 1 + quantum/xap/xap_handlers.c | 12 ++++++++++++ 7 files changed, 44 insertions(+), 4 deletions(-) diff --git a/data/xap/xap_0.1.0.hjson b/data/xap/xap_0.1.0.hjson index c4988a224648..eef1b05e0dda 100755 --- a/data/xap/xap_0.1.0.hjson +++ b/data/xap/xap_0.1.0.hjson @@ -321,7 +321,7 @@ ''' Jump to bootloader - May not be present – if QMK capabilities query returns “true”, then jump to bootloader is supported + 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 @@ -337,6 +337,24 @@ return_type: u32[4] return_execute: get_hardware_id } + 0x09: { + type: command + name: Reinitialize EEPROM + define: EEPROM_RESET + permissions: secure + enable_if_preprocessor: !defined(NO_RESET) + description: + ''' + Reinitializes the keyboard's EEPROM (persistent memory) + + May not be present - if QMK capabilities query returns “true”, then reinitialize is supported + + * 0 means secure routes are disabled, and should be considered as a failure + * 1 means successful, board will reinitialize and then reboot + ''' + return_type: u8 + return_execute: request_eeprom_reset + } } }, diff --git a/docs/xap_0.1.0.md b/docs/xap_0.1.0.md index 3fa4dc00ff91..de34463df2f6 100644 --- a/docs/xap_0.1.0.md +++ b/docs/xap_0.1.0.md @@ -111,8 +111,9 @@ This subsystem is always present, and provides the ability to address QMK-specif | Product Name | `0x01 0x04` | |__Response:__ `string`| Retrieves the product name| | Config Blob Length | `0x01 0x05` | |__Response:__ `u16`| Retrieves the length of the configuration data bundled within the firmware| | Config Blob Chunk | `0x01 0x06` | |__Request:__ `u16`

__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

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| +| Jump to bootloader | `0x01 0x07` | __Secure__ |__Response:__ `u8`| 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| | Hardware Identifier | `0x01 0x08` | |__Response:__ `u32[4]`| Retrieves a unique identifier for the board.| +| Reinitialize EEPROM | `0x01 0x09` | __Secure__ |__Response:__ `u8`| Reinitializes the keyboard's EEPROM (persistent memory)

May not be present - if QMK capabilities query returns “true”, then reinitialize is supported

* 0 means secure routes are disabled, and should be considered as a failure
* 1 means successful, board will reinitialize and then reboot| ### Keyboard - `0x02` This subsystem is always present, and reserved for vendor-specific functionality. No routes are defined by XAP. diff --git a/docs/xap_0.2.0.md b/docs/xap_0.2.0.md index 560e141fdd25..cfdece98917e 100644 --- a/docs/xap_0.2.0.md +++ b/docs/xap_0.2.0.md @@ -111,8 +111,9 @@ This subsystem is always present, and provides the ability to address QMK-specif | Product Name | `0x01 0x04` | |__Response:__ `string`| Retrieves the product name| | Config Blob Length | `0x01 0x05` | |__Response:__ `u16`| Retrieves the length of the configuration data bundled within the firmware| | Config Blob Chunk | `0x01 0x06` | |__Request:__ `u16`

__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

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| +| Jump to bootloader | `0x01 0x07` | __Secure__ |__Response:__ `u8`| 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| | Hardware Identifier | `0x01 0x08` | |__Response:__ `u32[4]`| Retrieves a unique identifier for the board.| +| Reinitialize EEPROM | `0x01 0x09` | __Secure__ |__Response:__ `u8`| Reinitializes the keyboard's EEPROM (persistent memory)

May not be present - if QMK capabilities query returns “true”, then reinitialize is supported

* 0 means secure routes are disabled, and should be considered as a failure
* 1 means successful, board will reinitialize and then reboot| ### Keyboard - `0x02` This subsystem is always present, and reserved for vendor-specific functionality. No routes are defined by XAP. diff --git a/docs/xap_0.3.0.md b/docs/xap_0.3.0.md index ed4719277275..0b0954a160a6 100644 --- a/docs/xap_0.3.0.md +++ b/docs/xap_0.3.0.md @@ -111,8 +111,9 @@ This subsystem is always present, and provides the ability to address QMK-specif | Product Name | `0x01 0x04` | |__Response:__ `string`| Retrieves the product name| | Config Blob Length | `0x01 0x05` | |__Response:__ `u16`| Retrieves the length of the configuration data bundled within the firmware| | Config Blob Chunk | `0x01 0x06` | |__Request:__ `u16`

__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

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| +| Jump to bootloader | `0x01 0x07` | __Secure__ |__Response:__ `u8`| 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| | Hardware Identifier | `0x01 0x08` | |__Response:__ `u32[4]`| Retrieves a unique identifier for the board.| +| Reinitialize EEPROM | `0x01 0x09` | __Secure__ |__Response:__ `u8`| Reinitializes the keyboard's EEPROM (persistent memory)

May not be present - if QMK capabilities query returns “true”, then reinitialize is supported

* 0 means secure routes are disabled, and should be considered as a failure
* 1 means successful, board will reinitialize and then reboot| ### Keyboard - `0x02` This subsystem is always present, and reserved for vendor-specific functionality. No routes are defined by XAP. diff --git a/lib/python/xap_client/device.py b/lib/python/xap_client/device.py index 8196b6517e47..8844f98b1e3d 100644 --- a/lib/python/xap_client/device.py +++ b/lib/python/xap_client/device.py @@ -225,3 +225,9 @@ def reset(self): """ status = self.int_transaction(XAPRoutes.QMK_BOOTLOADER_JUMP) return status == 1 + + def reinit(self): + """Request device reset EEPROM - Requires previous unlock + """ + status = self.int_transaction(XAPRoutes.QMK_EEPROM_RESET) + return status == 1 diff --git a/lib/python/xap_client/routes.py b/lib/python/xap_client/routes.py index c8ebb415f894..a278d29ee402 100644 --- a/lib/python/xap_client/routes.py +++ b/lib/python/xap_client/routes.py @@ -49,6 +49,7 @@ class XAPRoutes(): QMK_CONFIG_BLOB_CHUNK = b'\x01\x06' QMK_BOOTLOADER_JUMP = b'\x01\x07' QMK_HARDWARE_ID = b'\x01\x08' + QMK_EEPROM_RESET = b'\x01\x09' # KEYMAP KEYMAP_CAPABILITIES_QUERY = b'\x04\x01' KEYMAP_GET_LAYER_COUNT = b'\x04\x02' diff --git a/quantum/xap/xap_handlers.c b/quantum/xap/xap_handlers.c index c729949ff5ea..38a50c95b89d 100644 --- a/quantum/xap/xap_handlers.c +++ b/quantum/xap/xap_handlers.c @@ -100,6 +100,18 @@ bool xap_respond_request_bootloader_jump(xap_token_t token, const void *data, si } #endif +#ifndef NO_RESET +bool xap_respond_request_eeprom_reset(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)); + eeconfig_disable(); + soft_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)); From 309a08ff8f5b20d10d4a2b84cf463e8b25d29c2a Mon Sep 17 00:00:00 2001 From: zvecr Date: Sun, 2 Oct 2022 00:23:31 +0100 Subject: [PATCH 138/203] Poential fix for pre gcc 8.1 --- .../qmk/xap/gen_firmware/inline_generator.py | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/python/qmk/xap/gen_firmware/inline_generator.py b/lib/python/qmk/xap/gen_firmware/inline_generator.py index c987fdda1609..79ec427fce0b 100755 --- a/lib/python/qmk/xap/gen_firmware/inline_generator.py +++ b/lib/python/qmk/xap/gen_firmware/inline_generator.py @@ -26,19 +26,19 @@ def _get_lighting_spec(xap_defs, feature): - version = xap_defs["uses"][feature] + version = xap_defs['uses'][feature] spec = json_load(Path(f'data/constants/{feature}_{version}.json')) # preprocess for gross rgblight "mode + n" for obj in spec.get('effects', {}).values(): - define = obj["key"] + define = obj['key'] offset = 0 - found = re.match("(.*)_(\\d+)$", define) + found = re.match('(.*)_(\\d+)$', define) if found: define = found.group(1) offset = int(found.group(2)) - 1 - obj["define"] = define - obj["offset"] = offset + obj['define'] = define + obj['offset'] = offset return spec @@ -322,15 +322,15 @@ def _append_lighting_map(lines, feature, spec): lines.append(f'static uint8_t {feature}_effect_map[][2] = {{') for id, obj in spec.get('effects', {}).items(): - define = obj["define"] - offset = f' + {obj["offset"]}' if obj["offset"] else '' + define = obj['define'] + offset = f' + {obj["offset"]}' if obj['offset'] else '' line = f''' #ifdef {ifdef_prefix}_{define} {{ {id}, {def_prefix}_{define}{offset}}}, #endif''' - group = groups.get(obj.get("group", None), {}).get('define', None) + group = groups.get(obj.get('group', None), {}).get('define', None) if group: line = f''' #ifdef {group} @@ -368,16 +368,16 @@ def _append_lighting_bitmask(lines, feature, spec): groups = spec.get('groups', {}) ifdef_prefix = PREFIX_MAP[feature]['ifdef'] - lines.append(f"static const uint64_t ENABLED_{feature.upper()}_EFFECTS = 0") + lines.append(f'enum {{ ENABLED_{feature.upper()}_EFFECTS = 0') for id, obj in spec.get('effects', {}).items(): - define = obj["define"] + define = obj['define'] line = f''' #ifdef {ifdef_prefix}_{define} | (1ULL << {id}) #endif''' - group = groups.get(obj.get("group", None), {}).get('define', None) + group = groups.get(obj.get('group', None), {}).get('define', None) if group: line = f''' #ifdef {group} @@ -386,7 +386,7 @@ def _append_lighting_bitmask(lines, feature, spec): lines.append(line) - lines.append(';') + lines.append('};') def _append_lighting_mapping(lines, xap_defs): @@ -402,9 +402,9 @@ def _append_lighting_mapping(lines, xap_defs): for feature in PREFIX_MAP.keys(): spec = _get_lighting_spec(xap_defs, feature) - lines.append(f"#ifdef {feature.upper()}_ENABLE") + lines.append(f'#ifdef {feature.upper()}_ENABLE') _append_lighting_map(lines, feature, spec) - lines.append(f"#endif //{feature.upper()}_ENABLE") + lines.append(f'#endif //{feature.upper()}_ENABLE') # TODO: should be inside ifdef but causes build issues _append_lighting_bitmask(lines, feature, spec) From 77a86b018a35f1c2ab9f5f0bd243c5e5f5b50ee6 Mon Sep 17 00:00:00 2001 From: zvecr Date: Sun, 2 Oct 2022 00:53:05 +0100 Subject: [PATCH 139/203] Resolve ifdef todo --- lib/python/qmk/xap/gen_firmware/inline_generator.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/python/qmk/xap/gen_firmware/inline_generator.py b/lib/python/qmk/xap/gen_firmware/inline_generator.py index 79ec427fce0b..334c53ddd040 100755 --- a/lib/python/qmk/xap/gen_firmware/inline_generator.py +++ b/lib/python/qmk/xap/gen_firmware/inline_generator.py @@ -107,9 +107,12 @@ def _append_routing_table_declaration(lines, container, container_id, route_stac route_name = to_snake('_'.join([r['define'] for r in route_stack])) + condition = route_conditions(route_stack) + if condition: + lines.append(f'#if {condition}') + 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);') @@ -165,6 +168,10 @@ def _append_routing_table_declaration(lines, container, container_id, route_stac elif container['return_type'] == 'struct': pass + if condition: + lines.append(f'#endif // {condition}') + lines.append('') + route_stack.pop() @@ -404,10 +411,8 @@ def _append_lighting_mapping(lines, xap_defs): lines.append(f'#ifdef {feature.upper()}_ENABLE') _append_lighting_map(lines, feature, spec) - lines.append(f'#endif //{feature.upper()}_ENABLE') - - # TODO: should be inside ifdef but causes build issues _append_lighting_bitmask(lines, feature, spec) + lines.append(f'#endif //{feature.upper()}_ENABLE') def generate_inline(output_file, keyboard, keymap): From 986534ea958e8474601210ff666fbf1185dc4976 Mon Sep 17 00:00:00 2001 From: zvecr Date: Mon, 3 Oct 2022 01:04:46 +0100 Subject: [PATCH 140/203] Stub out lighting sets --- data/xap/xap_0.3.0.hjson | 118 ++++++++++++++++++++++++++++++++ docs/xap_0.3.0.md | 6 ++ lib/python/xap_client/routes.py | 6 ++ lib/python/xap_client/types.py | 3 + quantum/xap/xap_handlers.c | 98 ++++++++++++++++++++++++++ 5 files changed, 231 insertions(+) diff --git a/data/xap/xap_0.3.0.hjson b/data/xap/xap_0.3.0.hjson index 6cb13f9455ef..81f881bb0772 100644 --- a/data/xap/xap_0.3.0.hjson +++ b/data/xap/xap_0.3.0.hjson @@ -65,6 +65,36 @@ ] return_execute: get_backlight_config } + 0x04: { + type: command + name: Set Config + define: SET_CONFIG + description: Set the current config. + request_type: struct + request_struct_length: 3 + request_struct_members: [ + { + type: u8 + name: enable + }, + { + type: u8 + name: mode + }, + { + type: u8 + name: val + }, + ] + return_execute: set_backlight_config + } + 0x05: { + type: command + name: Save Config + define: SAVE_CONFIG + description: Save the current config. + return_execute: save_backlight_config + } } } @@ -133,6 +163,48 @@ ] return_execute: get_rgblight_config } + 0x04: { + type: command + name: Set Config + define: SET_CONFIG + description: Set the current config. + request_type: struct + request_struct_length: 6 + request_struct_members: [ + { + type: u8 + name: enable + }, + { + type: u8 + name: mode + }, + { + type: u8 + name: hue + }, + { + type: u8 + name: sat + }, + { + type: u8 + name: val + }, + { + type: u8 + name: speed + }, + ] + return_execute: set_rgblight_config + } + 0x05: { + type: command + name: Save Config + define: SAVE_CONFIG + description: Save the current config. + return_execute: save_rgblight_config + } } } @@ -205,6 +277,52 @@ ] return_execute: get_rgb_matrix_config } + 0x04: { + type: command + name: Set Config + define: SET_CONFIG + description: Set the current config. + request_type: struct + request_struct_length: 7 + request_struct_members: [ + { + type: u8 + name: enable + }, + { + type: u8 + name: mode + }, + { + type: u8 + name: hue + }, + { + type: u8 + name: sat + }, + { + type: u8 + name: val + }, + { + type: u8 + name: speed + }, + { + type: u8 + name: flags + }, + ] + return_execute: set_rgb_matrix_config + } + 0x05: { + type: command + name: Save Config + define: SAVE_CONFIG + description: Save the current config. + return_execute: save_rgb_matrix_config + } } } } diff --git a/docs/xap_0.3.0.md b/docs/xap_0.3.0.md index 0b0954a160a6..8cc054859167 100644 --- a/docs/xap_0.3.0.md +++ b/docs/xap_0.3.0.md @@ -161,6 +161,8 @@ This subsystem allows for control over the backlight subsystem. | Capabilities Query | `0x06 0x02 0x01` | |__Response:__ `u32`| backlight subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.| | Get Enabled Effects | `0x06 0x02 0x02` | |__Response:__ `u8`| Each bit should be considered as a "usable" effect id| | Get Config | `0x06 0x02 0x03` | |__Response:__
    * enable: `u8`
    * mode: `u8`
    * val: `u8`| Query the current config.| +| Set Config | `0x06 0x02 0x04` | |__Request:__
    * enable: `u8`
    * mode: `u8`
    * val: `u8`| Set the current config.| +| Save Config | `0x06 0x02 0x05` | || Save the current config.| #### rgblight - `0x06 0x03` This subsystem allows for control over the rgblight subsystem. @@ -170,6 +172,8 @@ This subsystem allows for control over the rgblight subsystem. | Capabilities Query | `0x06 0x03 0x01` | |__Response:__ `u32`| rgblight subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.| | Get Enabled Effects | `0x06 0x03 0x02` | |__Response:__ `u64`| Each bit should be considered as a "usable" effect id| | Get Config | `0x06 0x03 0x03` | |__Response:__
    * enable: `u8`
    * mode: `u8`
    * hue: `u8`
    * sat: `u8`
    * val: `u8`
    * speed: `u8`| Query the current config.| +| Set Config | `0x06 0x03 0x04` | |__Request:__
    * enable: `u8`
    * mode: `u8`
    * hue: `u8`
    * sat: `u8`
    * val: `u8`
    * speed: `u8`| Set the current config.| +| Save Config | `0x06 0x03 0x05` | || Save the current config.| #### rgbmatrix - `0x06 0x04` This subsystem allows for control over the rgb matrix subsystem. @@ -179,6 +183,8 @@ This subsystem allows for control over the rgb matrix subsystem. | Capabilities Query | `0x06 0x04 0x01` | |__Response:__ `u32`| rgb matrix subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.| | Get Enabled Effects | `0x06 0x04 0x02` | |__Response:__ `u64`| Each bit should be considered as a "usable" effect id| | Get Config | `0x06 0x04 0x03` | |__Response:__
    * enable: `u8`
    * mode: `u8`
    * hue: `u8`
    * sat: `u8`
    * val: `u8`
    * speed: `u8`
    * flags: `u8`| Query the current config.| +| Set Config | `0x06 0x04 0x04` | |__Request:__
    * enable: `u8`
    * mode: `u8`
    * hue: `u8`
    * sat: `u8`
    * val: `u8`
    * speed: `u8`
    * flags: `u8`| Set the current config.| +| Save Config | `0x06 0x04 0x05` | || Save the current config.| ## Broadcast messages diff --git a/lib/python/xap_client/routes.py b/lib/python/xap_client/routes.py index a278d29ee402..cace9b1591ac 100644 --- a/lib/python/xap_client/routes.py +++ b/lib/python/xap_client/routes.py @@ -66,11 +66,17 @@ class XAPRoutes(): LIGHTING_BACKLIGHT_CAPABILITIES_QUERY = b'\x06\x02\x01' LIGHTING_BACKLIGHT_GET_ENABLED_EFFECTS = b'\x06\x02\x02' LIGHTING_BACKLIGHT_GET_CONFIG = b'\x06\x02\x03' + LIGHTING_BACKLIGHT_SET_CONFIG = b'\x06\x02\x04' + LIGHTING_BACKLIGHT_SAVE_CONFIG = b'\x06\x02\x05' LIGHTING_RGBLIGHT = b'\x06\x03' LIGHTING_RGBLIGHT_CAPABILITIES_QUERY = b'\x06\x03\x01' LIGHTING_RGBLIGHT_GET_ENABLED_EFFECTS = b'\x06\x03\x02' LIGHTING_RGBLIGHT_GET_CONFIG = b'\x06\x03\x03' + LIGHTING_RGBLIGHT_SET_CONFIG = b'\x06\x03\x04' + LIGHTING_RGBLIGHT_SAVE_CONFIG = b'\x06\x03\x05' LIGHTING_RGB_MATRIX = b'\x06\x04' LIGHTING_RGB_MATRIX_CAPABILITIES_QUERY = b'\x06\x04\x01' LIGHTING_RGB_MATRIX_GET_ENABLED_EFFECTS = b'\x06\x04\x02' LIGHTING_RGB_MATRIX_GET_CONFIG = b'\x06\x04\x03' + LIGHTING_RGB_MATRIX_SET_CONFIG = b'\x06\x04\x04' + LIGHTING_RGB_MATRIX_SAVE_CONFIG = b'\x06\x04\x05' diff --git a/lib/python/xap_client/types.py b/lib/python/xap_client/types.py index 60416795ce26..7ad25fc0d123 100644 --- a/lib/python/xap_client/types.py +++ b/lib/python/xap_client/types.py @@ -108,8 +108,11 @@ def to_bytes(self): # TODO: gen inbound object for set_keymap_keycode # TODO: gen inbound object for set_encoder_keycode # TODO: gen outbound object for get_config +# TODO: gen inbound object for set_config # TODO: gen outbound object for get_config +# TODO: gen inbound object for set_config # TODO: gen outbound object for get_config +# TODO: gen inbound object for set_config class XAPSecureStatus(IntEnum): diff --git a/quantum/xap/xap_handlers.c b/quantum/xap/xap_handlers.c index 38a50c95b89d..2938892d508d 100644 --- a/quantum/xap/xap_handlers.c +++ b/quantum/xap/xap_handlers.c @@ -211,6 +211,37 @@ bool xap_respond_get_backlight_config(xap_token_t token, const void *data, size_ return xap_respond_data(token, &ret, sizeof(ret)); } +bool xap_respond_set_backlight_config(xap_token_t token, const void *data, size_t length) { + if (length != sizeof(xap_route_lighting_backlight_set_config_arg_t)) { + return false; + } + + xap_route_lighting_backlight_set_config_arg_t *arg = (xap_route_lighting_backlight_set_config_arg_t *)data; + + if (arg->enable) { + backlight_level_noeeprom(arg->val); + } else { + backlight_level_noeeprom(0); + } + +# ifdef BACKLIGHT_BREATHING + if (arg->mode) { + backlight_enable_breathing(); + } else { + backlight_disable_breathing(); + } +# endif + + xap_respond_success(token); + return true; +} + +bool xap_respond_save_backlight_config(xap_token_t token, const void *data, size_t length) { + eeconfig_update_backlight_current(); + + xap_respond_success(token); + return true; +} #endif #if ((defined(RGBLIGHT_ENABLE))) @@ -219,6 +250,11 @@ bool xap_respond_get_backlight_config(xap_token_t token, const void *data, size_ extern rgblight_config_t rgblight_config; uint8_t rgblight2xap(uint8_t val); +uint8_t xap2rgblight(uint8_t val); + +void rgblight_enabled_noeeprom(bool val) { + val ? rgblight_enable_noeeprom() : rgblight_disable_noeeprom(); +} bool xap_respond_get_rgblight_config(xap_token_t token, const void *data, size_t length) { xap_route_lighting_rgblight_get_config_t ret; @@ -232,6 +268,34 @@ bool xap_respond_get_rgblight_config(xap_token_t token, const void *data, size_t return xap_respond_data(token, &ret, sizeof(ret)); } + +bool xap_respond_set_rgblight_config(xap_token_t token, const void *data, size_t length) { + if (length != sizeof(xap_route_lighting_rgblight_set_config_arg_t)) { + return false; + } + + xap_route_lighting_rgblight_set_config_arg_t *arg = (xap_route_lighting_rgblight_set_config_arg_t *)data; + + uint8_t mode = xap2rgblight(arg->mode); + if (mode == INVALID_EFFECT) { + return false; + } + + rgblight_enabled_noeeprom(arg->enable); + rgblight_mode_noeeprom(mode); + rgblight_sethsv_noeeprom(arg->hue, arg->sat, arg->val); + rgblight_set_speed_noeeprom(arg->speed); + + xap_respond_success(token); + return true; +} + +bool xap_respond_save_rgblight_config(xap_token_t token, const void *data, size_t length) { + eeconfig_update_rgblight_current(); + + xap_respond_success(token); + return true; +} #endif #if ((defined(RGB_MATRIX_ENABLE))) @@ -240,6 +304,11 @@ bool xap_respond_get_rgblight_config(xap_token_t token, const void *data, size_t extern rgb_config_t rgb_matrix_config; uint8_t rgb_matrix2xap(uint8_t val); +uint8_t xap2rgb_matrix(uint8_t val); + +void rgb_matrix_enabled_noeeprom(bool val) { + val ? rgb_matrix_enable_noeeprom() : rgb_matrix_disable_noeeprom(); +} bool xap_respond_get_rgb_matrix_config(xap_token_t token, const void *data, size_t length) { xap_route_lighting_rgb_matrix_get_config_t ret; @@ -254,4 +323,33 @@ bool xap_respond_get_rgb_matrix_config(xap_token_t token, const void *data, size return xap_respond_data(token, &ret, sizeof(ret)); } + +bool xap_respond_set_rgb_matrix_config(xap_token_t token, const void *data, size_t length) { + if (length != sizeof(xap_route_lighting_rgb_matrix_set_config_arg_t)) { + return false; + } + + xap_route_lighting_rgb_matrix_set_config_arg_t *arg = (xap_route_lighting_rgb_matrix_set_config_arg_t *)data; + + uint8_t mode = xap2rgb_matrix(arg->mode); + if (mode == INVALID_EFFECT) { + return false; + } + + rgb_matrix_enabled_noeeprom(arg->enable); + rgb_matrix_mode_noeeprom(mode); + rgb_matrix_sethsv_noeeprom(arg->hue, arg->sat, arg->val); + rgb_matrix_set_speed_noeeprom(arg->speed); + rgb_matrix_set_flags(arg->flags); + + xap_respond_success(token); + return true; +} + +bool xap_respond_save_rgb_matrix_config(xap_token_t token, const void *data, size_t length) { + eeconfig_update_rgb_matrix(); + + xap_respond_success(token); + return true; +} #endif From 907640e40ead34649c64c51fc79646b64981876f Mon Sep 17 00:00:00 2001 From: Stefan Kerkmann Date: Wed, 5 Oct 2022 21:26:53 +0200 Subject: [PATCH 141/203] [Bug] XAP: Fix unaligned memory access in config blob handler and USB task loop condition (#18612) * Fix unaligned memory access in config blob handler data* points in the middle of an u8 array, casting this to an u16* and dereferencing it leads to an unaligned memory access - which hardfaults on Cortex M0 mcus e.g. RP2040s. * Actually read until there is no more data to be read --- quantum/xap/xap_handlers.c | 3 ++- tmk_core/protocol/chibios/usb_main.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/quantum/xap/xap_handlers.c b/quantum/xap/xap_handlers.c index 2938892d508d..3c3bbdace7f4 100644 --- a/quantum/xap/xap_handlers.c +++ b/quantum/xap/xap_handlers.c @@ -64,7 +64,8 @@ bool xap_respond_get_config_blob_chunk(xap_token_t token, const void *data, size return false; } - uint16_t offset = *((uint16_t *)data); + uint16_t offset; + memcpy(&offset, data, sizeof(uint16_t)); xap_route_qmk_config_blob_chunk_t ret = {0}; diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index 795cc1443814..18c7d3f246ef 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -1165,7 +1165,7 @@ 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); + size = chnReadTimeout(&drivers.xap_driver.driver, buffer, sizeof(buffer), TIME_IMMEDIATE); if (size > 0) { xap_receive_base(buffer); } From f7b44473aab229ccaa7e210dcc0ac683070418b3 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Sat, 8 Oct 2022 15:12:10 +1100 Subject: [PATCH 142/203] Use introspection handlers. --- quantum/xap/xap_handlers.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/quantum/xap/xap_handlers.c b/quantum/xap/xap_handlers.c index 3c3bbdace7f4..565dddc4cd06 100644 --- a/quantum/xap/xap_handlers.c +++ b/quantum/xap/xap_handlers.c @@ -134,9 +134,7 @@ bool xap_respond_get_keymap_keycode(xap_token_t token, const void *data, size_t return false; } - keypos_t pos = MAKE_KEYPOS(arg->row, arg->column); - - uint16_t keycode = keymap_key_to_keycode(arg->layer, pos); + uint16_t keycode = keycode_at_keymap_location(arg->layer, arg->row, arg->column); return xap_respond_data(token, &keycode, sizeof(keycode)); } @@ -152,9 +150,7 @@ bool xap_respond_get_encoder_keycode(xap_token_t token, const void *data, size_t return false; } - 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); + uint16_t keycode = keycode_at_encodermap_location(arg->layer, arg->encoder, arg->clockwise); return xap_respond_data(token, &keycode, sizeof(keycode)); } #endif From a53b01772e1348458e81ed9cadbf187177121982 Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 12 Oct 2022 00:09:27 +0100 Subject: [PATCH 143/203] Dummy client config --- lib/python/xap_client/pyproject.toml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 lib/python/xap_client/pyproject.toml diff --git a/lib/python/xap_client/pyproject.toml b/lib/python/xap_client/pyproject.toml new file mode 100644 index 000000000000..4f689ac7e184 --- /dev/null +++ b/lib/python/xap_client/pyproject.toml @@ -0,0 +1,7 @@ +[project] +name = "xap_client" +description = "XAP Client" +version = "0.0.0" +dependencies = [ + "hid ~= 1.0.5", +] From f3f9b2b08c2bf815dff43de22a2116888046dbd1 Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 12 Oct 2022 01:23:00 +0100 Subject: [PATCH 144/203] Dummy client config - more config --- lib/python/xap_client/pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/python/xap_client/pyproject.toml b/lib/python/xap_client/pyproject.toml index 4f689ac7e184..94b53db5562d 100644 --- a/lib/python/xap_client/pyproject.toml +++ b/lib/python/xap_client/pyproject.toml @@ -5,3 +5,7 @@ version = "0.0.0" dependencies = [ "hid ~= 1.0.5", ] + +[tool.setuptools] +packages = ['xap_client'] +package-dir = { 'xap_client' = '.' } From 9e1b8a710c4b3b8bc27bf0d501ffbe891795ecda Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 12 Oct 2022 19:41:11 +0100 Subject: [PATCH 145/203] Refactor XAP keycode logic --- ...ycodes_0.0.1.json => keycodes_0.0.1.hjson} | 14 ++--- data/schemas/xap.jsonschema | 6 --- data/xap/xap_0.1.0.hjson | 4 -- data/xap/xap_0.3.0.hjson | 6 --- lib/python/qmk/cli/xap/xap.py | 17 ++++-- lib/python/qmk/keycodes.py | 54 +++++++++++++++++++ lib/python/qmk/xap/common.py | 16 +----- 7 files changed, 75 insertions(+), 42 deletions(-) rename data/constants/{keycodes_0.0.1.json => keycodes_0.0.1.hjson} (99%) create mode 100644 lib/python/qmk/keycodes.py diff --git a/data/constants/keycodes_0.0.1.json b/data/constants/keycodes_0.0.1.hjson similarity index 99% rename from data/constants/keycodes_0.0.1.json rename to data/constants/keycodes_0.0.1.hjson index f3ff0166e3cd..72bed1ab2332 100644 --- a/data/constants/keycodes_0.0.1.json +++ b/data/constants/keycodes_0.0.1.hjson @@ -817,7 +817,7 @@ "key": "KC_LOCKING_NUM_LOCK", "label": "Num Lock", "aliases": [ - "KC_LSCR" + "KC_LNUM" ] }, "0x0084": { @@ -825,7 +825,7 @@ "key": "KC_LOCKING_SCROLL_LOCK", "label": "Scroll Lock", "aliases": [ - "KC_LCAP" + "KC_LSCR" ] }, "0x0085": { @@ -1246,23 +1246,23 @@ "key": "KC_WWW_FAVORITES", "label": "Browser Favorites", "aliases": [ - "KC_WREF" + "KC_WFAV" ] }, "0x00bb": { "group": "media", "key": "KC_MEDIA_FAST_FORWARD", - "label": "Browser Favorites", + "label": "Next Track", "aliases": [ - "KC_WREF" + "KC_MFFD" ] }, "0x00bc": { "group": "media", "key": "KC_MEDIA_REWIND", - "label": "Browser Favorites", + "label": "Previous Track", "aliases": [ - "KC_WFAV" + "KC_MRWD" ] }, "0x00bd": { diff --git a/data/schemas/xap.jsonschema b/data/schemas/xap.jsonschema index 0e3189561f06..89000bb47f75 100644 --- a/data/schemas/xap.jsonschema +++ b/data/schemas/xap.jsonschema @@ -135,12 +135,6 @@ "define": { "$ref": "qmk.definitions.v1#/define" }, - "uses": { - "type": "object", - "additionalProperties": { - "$ref": "qmk.definitions.v1#/bcd_version" - } - }, "documentation": { "type": "object", "properties": { diff --git a/data/xap/xap_0.1.0.hjson b/data/xap/xap_0.1.0.hjson index eef1b05e0dda..366f2cbf3185 100755 --- a/data/xap/xap_0.1.0.hjson +++ b/data/xap/xap_0.1.0.hjson @@ -1,10 +1,6 @@ { version: 0.1.0 - uses: { - keycodes: 0.0.1 - } - documentation: { order: [ broadcast_messages diff --git a/data/xap/xap_0.3.0.hjson b/data/xap/xap_0.3.0.hjson index 81f881bb0772..1bd24c47f2d1 100644 --- a/data/xap/xap_0.3.0.hjson +++ b/data/xap/xap_0.3.0.hjson @@ -1,12 +1,6 @@ { version: 0.3.0 - uses: { - rgblight: 0.0.1 - rgb_matrix: 0.0.1 - led_matrix: 0.0.1 - } - routes: { 0x06: { diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index f1f28f86ce02..e6643be65c8d 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -4,13 +4,12 @@ from milc import cli +from qmk.keycodes import load_spec +from qmk.decorators import lru_cache from qmk.keyboard import render_layout -from qmk.xap.common import get_xap_keycodes from xap_client import XAPClient, XAPEventType, XAPSecureStatus, XAPConfigRgblight, XAPConfigBacklight, XAPConfigRgbMatrix, XAPRoutes -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. @@ -38,6 +37,16 @@ def print_dotted_output(kb_info_json, prefix=''): cli.echo(' {fg_blue}%s{fg_reset}: %s', new_prefix, kb_info_json[key]) +@lru_cache(timeout=5) +def _load_keycodes(keycode_version): + """Gets keycode data for the required version of the XAP definitions. + """ + spec = load_spec(keycode_version) + + # 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 _list_devices(): """Dump out available devices """ @@ -62,7 +71,7 @@ def __init__(self, device): cmd.Cmd.__init__(self) self.device = device # cache keycodes for this device - self.keycodes = get_xap_keycodes(device.version()['xap']) + self.keycodes = _load_keycodes(device.version().get('keycodes', 'latest')) def do_about(self, arg): """Prints out the version info of QMK diff --git a/lib/python/qmk/keycodes.py b/lib/python/qmk/keycodes.py new file mode 100644 index 000000000000..85aa77be383a --- /dev/null +++ b/lib/python/qmk/keycodes.py @@ -0,0 +1,54 @@ +from pathlib import Path + +from qmk.json_schema import deep_update, json_load + +CONSTANTS_PATH = Path('data/constants/') + + +def _validate(spec): + # no duplicate keycodes + keycodes = [] + for value in spec['keycodes'].values(): + keycodes.append(value['key']) + keycodes.extend(value.get('aliases', [])) + duplicates = set([x for x in keycodes if keycodes.count(x) > 1]) + if duplicates: + raise ValueError(f'Keycode spec contains duplicate keycodes! ({",".join(duplicates)})') + + +def load_spec(version): + """Build keycode data from the requested spec file + """ + if version == 'latest': + version = list_versions()[0] + + file = CONSTANTS_PATH / f'keycodes_{version}.hjson' + if not file.exists(): + raise ValueError(f'Requested keycode spec ({version}) is invalid!') + + # Load base + spec = json_load(file) + + # Merge in fragments + fragments = CONSTANTS_PATH.glob(f'keycodes_{version}_*.hjson') + for file in fragments: + deep_update(spec, json_load(file)) + + # Sort? + spec['keycodes'] = dict(sorted(spec['keycodes'].items())) + + # Validate? + _validate(spec) + + return spec + + +def list_versions(): + """Return available versions - sorted newest first + """ + ret = [] + for file in CONSTANTS_PATH.glob('keycodes_[0-9].[0-9].[0-9].hjson'): + ret.append(file.stem.split('_')[1]) + + ret.sort(reverse=True) + return ret diff --git a/lib/python/qmk/xap/common.py b/lib/python/qmk/xap/common.py index da888b09c8c7..9e00034f5cc9 100755 --- a/lib/python/qmk/xap/common.py +++ b/lib/python/qmk/xap/common.py @@ -9,7 +9,7 @@ from qmk.casing import to_snake from qmk.constants import QMK_FIRMWARE -from qmk.json_schema import json_load, validate +from qmk.json_schema import validate from qmk.decorators import lru_cache from qmk.keymap import locate_keymap from qmk.path import keyboard @@ -149,20 +149,6 @@ def merge_xap_defs(kb, km): 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. """ From dfc3ead42de97051c727974ff1ad00895b6f4f13 Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 12 Oct 2022 19:41:45 +0100 Subject: [PATCH 146/203] Ignore xap_client lint issues --- lib/python/qmk/cli/format/python.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/python/qmk/cli/format/python.py b/lib/python/qmk/cli/format/python.py index 008622cac163..b9a623e02f9f 100755 --- a/lib/python/qmk/cli/format/python.py +++ b/lib/python/qmk/cli/format/python.py @@ -12,7 +12,7 @@ def yapf_run(files): edit = '--diff' if cli.args.dry_run else '--in-place' - yapf_cmd = ['yapf', '-vv', '--recursive', edit, *files] + yapf_cmd = ['yapf', '-vv', '--exclude', '**/xap_client/*', '--recursive', edit, *files] try: cli.run(yapf_cmd, check=True, capture_output=False, stdin=DEVNULL) cli.log.info('Successfully formatted the python code.') From d1e3036ce2686cb874e3a74aba9ff5026bbe02d7 Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 13 Oct 2022 01:42:27 +0100 Subject: [PATCH 147/203] Generate python effect constants --- .gitignore | 1 + .../xap/client/python/constants.py.j2 | 14 ++ lib/python/qmk/cli/xap/generate_python.py | 2 +- lib/python/qmk/xap/common.py | 11 +- .../qmk/xap/gen_firmware/inline_generator.py | 6 +- lib/python/xap_client/__init__.py | 1 + lib/python/xap_client/constants.py | 122 ++++++++++++++++++ 7 files changed, 151 insertions(+), 6 deletions(-) create mode 100644 data/templates/xap/client/python/constants.py.j2 create mode 100644 lib/python/xap_client/constants.py diff --git a/.gitignore b/.gitignore index b268ab0d75c9..1ba894d2f940 100644 --- a/.gitignore +++ b/.gitignore @@ -82,6 +82,7 @@ secrets.tar # Python things __pycache__ .python-version +*.egg-info # Prerequisites for updating ChibiOS /util/fmpp* diff --git a/data/templates/xap/client/python/constants.py.j2 b/data/templates/xap/client/python/constants.py.j2 new file mode 100644 index 000000000000..61c9767d9709 --- /dev/null +++ b/data/templates/xap/client/python/constants.py.j2 @@ -0,0 +1,14 @@ +from enum import IntEnum + + +# version: 0.0.1 +class RgblightModes(IntEnum): +{%- for id, effect in constants.rgblight.effects | dictsort %} + {{ effect.key }} = {{ id }} +{%- endfor %} + + +class RgbMatrixModes(IntEnum): +{%- for id, effect in constants.rgb_matrix.effects | dictsort %} + {{ effect.key }} = {{ id }} +{%- endfor %} diff --git a/lib/python/qmk/cli/xap/generate_python.py b/lib/python/qmk/cli/xap/generate_python.py index c761f7d8247d..cd7573cca10b 100644 --- a/lib/python/qmk/cli/xap/generate_python.py +++ b/lib/python/qmk/cli/xap/generate_python.py @@ -13,7 +13,7 @@ def xap_generate_python(cli): defs = latest_xap_defs() parent = QMK_FIRMWARE / 'lib' / 'python' / 'xap_client' - for name in ['types.py', 'routes.py']: + for name in ['types.py', 'routes.py', 'constants.py']: lines = [GPL2_HEADER_SH_LIKE, GENERATED_HEADER_SH_LIKE] output = render_xap_output('client/python', f'{name}.j2', defs) diff --git a/lib/python/qmk/xap/common.py b/lib/python/qmk/xap/common.py index 9e00034f5cc9..a30b8d2f1ab1 100755 --- a/lib/python/qmk/xap/common.py +++ b/lib/python/qmk/xap/common.py @@ -9,7 +9,7 @@ from qmk.casing import to_snake from qmk.constants import QMK_FIRMWARE -from qmk.json_schema import validate +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 @@ -25,7 +25,14 @@ def _get_jinja2_env(data_templates_xap_subdir: str): 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), to_snake=to_snake) + + j2.globals['to_snake'] = to_snake + + constants = {} + for feature in ['rgblight', 'rgb_matrix']: + constants[feature] = json_load(Path(f'data/constants/{feature}_0.0.1.json')) + + return j2.get_template(file_to_render).render(xap=defs, xap_str=hjson.dumps(defs), constants=constants) def _find_kb_spec(kb): diff --git a/lib/python/qmk/xap/gen_firmware/inline_generator.py b/lib/python/qmk/xap/gen_firmware/inline_generator.py index 334c53ddd040..e61ec19f524a 100755 --- a/lib/python/qmk/xap/gen_firmware/inline_generator.py +++ b/lib/python/qmk/xap/gen_firmware/inline_generator.py @@ -25,8 +25,8 @@ } -def _get_lighting_spec(xap_defs, feature): - version = xap_defs['uses'][feature] +def _get_lighting_spec(feature): + version = '0.0.1' spec = json_load(Path(f'data/constants/{feature}_{version}.json')) # preprocess for gross rgblight "mode + n" @@ -407,7 +407,7 @@ def _append_lighting_mapping(lines, xap_defs): ''') for feature in PREFIX_MAP.keys(): - spec = _get_lighting_spec(xap_defs, feature) + spec = _get_lighting_spec(feature) lines.append(f'#ifdef {feature.upper()}_ENABLE') _append_lighting_map(lines, feature, spec) diff --git a/lib/python/xap_client/__init__.py b/lib/python/xap_client/__init__.py index 4c8affe1b88d..301f1c46fffe 100644 --- a/lib/python/xap_client/__init__.py +++ b/lib/python/xap_client/__init__.py @@ -3,3 +3,4 @@ from .types import * # noqa: F403 from .client import * # noqa: F403 from .routes import * # noqa: F403 +from .constants import * # noqa: F403 diff --git a/lib/python/xap_client/constants.py b/lib/python/xap_client/constants.py new file mode 100644 index 000000000000..09d69e69861a --- /dev/null +++ b/lib/python/xap_client/constants.py @@ -0,0 +1,122 @@ +# Copyright 2022 QMK +# SPDX-License-Identifier: GPL-2.0-or-later + +################################################################################ +# +# 88888888888 888 d8b .d888 d8b 888 d8b +# 888 888 Y8P d88P" Y8P 888 Y8P +# 888 888 888 888 +# 888 88888b. 888 .d8888b 888888 888 888 .d88b. 888 .d8888b +# 888 888 "88b 888 88K 888 888 888 d8P Y8b 888 88K +# 888 888 888 888 "Y8888b. 888 888 888 88888888 888 "Y8888b. +# 888 888 888 888 X88 888 888 888 Y8b. 888 X88 +# 888 888 888 888 88888P' 888 888 888 "Y8888 888 88888P' +# +# 888 888 +# 888 888 +# 888 888 +# .d88b. .d88b. 88888b. .d88b. 888d888 8888b. 888888 .d88b. .d88888 +# d88P"88b d8P Y8b 888 "88b d8P Y8b 888P" "88b 888 d8P Y8b d88" 888 +# 888 888 88888888 888 888 88888888 888 .d888888 888 88888888 888 888 +# Y88b 888 Y8b. 888 888 Y8b. 888 888 888 Y88b. Y8b. Y88b 888 +# "Y88888 "Y8888 888 888 "Y8888 888 "Y888888 "Y888 "Y8888 "Y88888 +# 888 +# Y8b d88P +# "Y88P" +# +################################################################################ + +from enum import IntEnum + + +# version: 0.0.1 +class RgblightModes(IntEnum): + STATIC_LIGHT = 0x00 + BREATHING = 0x01 + BREATHING_2 = 0x02 + BREATHING_3 = 0x03 + BREATHING_4 = 0x04 + RAINBOW_MOOD = 0x05 + RAINBOW_MOOD_2 = 0x06 + RAINBOW_MOOD_3 = 0x07 + RAINBOW_SWIRL = 0x08 + RAINBOW_SWIRL_2 = 0x09 + RAINBOW_SWIRL_3 = 0x0A + RAINBOW_SWIRL_4 = 0x0B + RAINBOW_SWIRL_5 = 0x0C + RAINBOW_SWIRL_6 = 0x0D + SNAKE = 0x0E + SNAKE_2 = 0x0F + SNAKE_3 = 0x10 + SNAKE_4 = 0x11 + SNAKE_5 = 0x12 + SNAKE_6 = 0x13 + KNIGHT = 0x14 + KNIGHT_2 = 0x15 + KNIGHT_3 = 0x16 + CHRISTMAS = 0x17 + STATIC_GRADIENT = 0x18 + STATIC_GRADIENT_2 = 0x19 + STATIC_GRADIENT_3 = 0x1A + STATIC_GRADIENT_4 = 0x1B + STATIC_GRADIENT_5 = 0x1C + STATIC_GRADIENT_6 = 0x1D + STATIC_GRADIENT_7 = 0x1E + STATIC_GRADIENT_8 = 0x1F + STATIC_GRADIENT_9 = 0x20 + STATIC_GRADIENT_10 = 0x21 + RGB_TEST = 0x22 + ALTERNATING = 0x23 + TWINKLE = 0x24 + TWINKLE_2 = 0x25 + TWINKLE_3 = 0x26 + TWINKLE_4 = 0x27 + TWINKLE_5 = 0x28 + TWINKLE_6 = 0x29 + + +class RgbMatrixModes(IntEnum): + SOLID_COLOR = 0x00 + ALPHAS_MODS = 0x01 + GRADIENT_UP_DOWN = 0x02 + GRADIENT_LEFT_RIGHT = 0x03 + BREATHING = 0x04 + BAND_SAT = 0x05 + BAND_VAL = 0x06 + BAND_PINWHEEL_SAT = 0x07 + BAND_PINWHEEL_VAL = 0x08 + BAND_SPIRAL_SAT = 0x09 + BAND_SPIRAL_VAL = 0x0A + CYCLE_ALL = 0x0B + CYCLE_LEFT_RIGHT = 0x0C + CYCLE_UP_DOWN = 0x0D + CYCLE_OUT_IN = 0x0E + CYCLE_OUT_IN_DUAL = 0x0F + RAINBOW_MOVING_CHEVRON = 0x10 + CYCLE_PINWHEEL = 0x11 + CYCLE_SPIRAL = 0x12 + DUAL_BEACON = 0x13 + RAINBOW_BEACON = 0x14 + RAINBOW_PINWHEELS = 0x15 + RAINDROPS = 0x16 + JELLYBEAN_RAINDROPS = 0x17 + HUE_BREATHING = 0x18 + HUE_PENDULUM = 0x19 + HUE_WAVE = 0x1A + PIXEL_FRACTAL = 0x1B + PIXEL_FLOW = 0x1C + PIXEL_RAIN = 0x1D + TYPING_HEATMAP = 0x1E + DIGITAL_RAIN = 0x1F + SOLID_REACTIVE_SIMPLE = 0x20 + SOLID_REACTIVE = 0x21 + SOLID_REACTIVE_WIDE = 0x22 + SOLID_REACTIVE_MULTIWIDE = 0x23 + SOLID_REACTIVE_CROSS = 0x24 + SOLID_REACTIVE_MULTICROSS = 0x25 + SOLID_REACTIVE_NEXUS = 0x26 + SOLID_REACTIVE_MULTINEXUS = 0x27 + SPLASH = 0x28 + MULTISPLASH = 0x29 + SOLID_SPLASH = 0x2A + SOLID_MULTISPLASH = 0x2B From 384bb7ddc75b5272192467d812d21871c61c0f09 Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 13 Oct 2022 02:21:15 +0100 Subject: [PATCH 148/203] Correctly handle 'latest' --- lib/python/qmk/xap/common.py | 38 ++++++++++++++++++- .../qmk/xap/gen_firmware/inline_generator.py | 26 +------------ 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/lib/python/qmk/xap/common.py b/lib/python/qmk/xap/common.py index a30b8d2f1ab1..dc3c53e02212 100755 --- a/lib/python/qmk/xap/common.py +++ b/lib/python/qmk/xap/common.py @@ -1,5 +1,6 @@ """This script handles the XAP protocol data files. """ +import re import os import hjson import jsonschema @@ -17,6 +18,39 @@ XAP_SPEC = 'xap.hjson' +def list_lighting_versions(feature): + """Return available versions - sorted newest first + """ + ret = [] + for file in Path('data/constants/').glob(f'{feature}_[0-9].[0-9].[0-9].json'): + ret.append(file.stem.split('_')[-1]) + + ret.sort(reverse=True) + return ret + + +def load_lighting_spec(feature, version='latest'): + """Build lighting data from the requested spec file + """ + if version == 'latest': + version = list_lighting_versions(feature)[0] + + spec = json_load(Path(f'data/constants/{feature}_{version}.json')) + + # preprocess for gross rgblight "mode + n" + for obj in spec.get('effects', {}).values(): + define = obj['key'] + offset = 0 + found = re.match('(.*)_(\\d+)$', define) + if found: + define = found.group(1) + offset = int(found.group(2)) - 1 + obj['define'] = define + obj['offset'] = offset + + return spec + + 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()) @@ -29,8 +63,8 @@ def render_xap_output(data_templates_xap_subdir, file_to_render, defs): j2.globals['to_snake'] = to_snake constants = {} - for feature in ['rgblight', 'rgb_matrix']: - constants[feature] = json_load(Path(f'data/constants/{feature}_0.0.1.json')) + for feature in ['rgblight', 'rgb_matrix', 'led_matrix']: + constants[feature] = load_lighting_spec(feature) return j2.get_template(file_to_render).render(xap=defs, xap_str=hjson.dumps(defs), constants=constants) diff --git a/lib/python/qmk/xap/gen_firmware/inline_generator.py b/lib/python/qmk/xap/gen_firmware/inline_generator.py index e61ec19f524a..78ceb2aa1d3f 100755 --- a/lib/python/qmk/xap/gen_firmware/inline_generator.py +++ b/lib/python/qmk/xap/gen_firmware/inline_generator.py @@ -1,13 +1,9 @@ """This script generates the XAP protocol generated header to be compiled into QMK. """ -import re -from pathlib import Path - 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 -from qmk.json_schema import json_load +from qmk.xap.common import merge_xap_defs, route_conditions, load_lighting_spec PREFIX_MAP = { 'rgblight': { @@ -25,24 +21,6 @@ } -def _get_lighting_spec(feature): - version = '0.0.1' - spec = json_load(Path(f'data/constants/{feature}_{version}.json')) - - # preprocess for gross rgblight "mode + n" - for obj in spec.get('effects', {}).values(): - define = obj['key'] - offset = 0 - found = re.match('(.*)_(\\d+)$', define) - if found: - define = found.group(1) - offset = int(found.group(2)) - 1 - obj['define'] = define - obj['offset'] = offset - - return spec - - def _get_c_type(xap_type): if xap_type == 'bool': return 'bool' @@ -407,7 +385,7 @@ def _append_lighting_mapping(lines, xap_defs): ''') for feature in PREFIX_MAP.keys(): - spec = _get_lighting_spec(feature) + spec = load_lighting_spec(feature) lines.append(f'#ifdef {feature.upper()}_ENABLE') _append_lighting_map(lines, feature, spec) From c2e95c85229b1449d200a1e5baa932ed904d4a29 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Sun, 16 Oct 2022 13:19:15 +1100 Subject: [PATCH 149/203] Squashed commit of the following: commit d614bc5f62f3c2efc5c5cc0f38168a67681e6fb5 Author: Nick Brassel Date: Sun Oct 16 13:17:03 2022 +1100 Remove old header generator. commit 08337b814cfcef57a1f6b41acf06b806ad4bb116 Author: Nick Brassel Date: Sat Oct 15 11:47:20 2022 +1100 Restart jinja2 generation for firmware-side output. --- data/templates/xap/client/python/types.py.j2 | 4 +- .../templates/xap/firmware/xap_generated.h.j2 | 166 +++++++++++ .../xap/firmware/xap_generated.inl.j2 | 9 + lib/python/qmk/cli/xap/generate_qmk.py | 14 +- lib/python/qmk/xap/common.py | 12 +- .../qmk/xap/gen_firmware/blob_generator.py | 8 +- .../qmk/xap/gen_firmware/header_generator.py | 268 ------------------ lib/python/qmk/xap/jinja2_filters.py | 70 +++++ 8 files changed, 274 insertions(+), 277 deletions(-) create mode 100755 data/templates/xap/firmware/xap_generated.h.j2 create mode 100755 data/templates/xap/firmware/xap_generated.inl.j2 delete mode 100755 lib/python/qmk/xap/gen_firmware/header_generator.py create mode 100644 lib/python/qmk/xap/jinja2_filters.py diff --git a/data/templates/xap/client/python/types.py.j2 b/data/templates/xap/client/python/types.py.j2 index 808f25bef260..ca9b90cdae55 100644 --- a/data/templates/xap/client/python/types.py.j2 +++ b/data/templates/xap/client/python/types.py.j2 @@ -34,10 +34,10 @@ class {{ name }}(namedtuple('{{ name }}', '{{ members }}')): {{ loop(item.routes.values()) }} {%- endif -%} {% if item.request_struct_members %} -# TODO: gen inbound object for {{ to_snake(item.define) }} +# TODO: gen inbound object for {{ item.define | to_snake }} {%- endif -%} {% if item.return_struct_members %} -# TODO: gen outbound object for {{ to_snake(item.define) }} +# TODO: gen outbound object for {{ item.define | to_snake }} {%- endif -%} {%- endfor %} diff --git a/data/templates/xap/firmware/xap_generated.h.j2 b/data/templates/xap/firmware/xap_generated.h.j2 new file mode 100755 index 000000000000..ef35bfacaa98 --- /dev/null +++ b/data/templates/xap/firmware/xap_generated.h.j2 @@ -0,0 +1,166 @@ +{{ GPL2_HEADER_C_LIKE }} +{{ GENERATED_HEADER_C_LIKE }} +#pragma once + +#include + +//////////////////////////////////////////////////////////////////////////////// +// Versions and identifiers + +#define XAP_BCD_VERSION UINT32_C({{ xap.version | triplet_to_bcd }}) +#define QMK_BCD_VERSION UINT32_C({{ qmk_version | triplet_to_bcd }}) +#define XAP_KEYBOARD_IDENTIFIER UINT32_C({{ keyboard | fnv1a_32 }}) + + +//////////////////////////////////////////////////////////////////////////////// +// Response flag definitions + +{% for bit,data in xap.response_flags.bits | dictsort -%} +#define {{ xap.response_flags.define_prefix }}_{{ data.define | to_snake | upper }} (UINT32_C(1) << ({{ bit }})) +{% endfor -%} +#define {{ xap.response_flags.define_prefix }}_FAILED 0x00 + + +//////////////////////////////////////////////////////////////////////////////// +// Broadcast message definitions + +{% for message_id,data in xap.broadcast_messages.messages | dictsort -%} +#define {{ xap.broadcast_messages.define_prefix }}_{{ data.define | to_snake | upper }} {{ message_id }} +{% if 'return_type' in data -%} +void {{ xap.broadcast_messages.define_prefix | lower }}_{{ data.define | to_snake | lower }}({{ data.return_type | type_to_c('value') }}); +{% else -%} +void {{ xap.broadcast_messages.define_prefix | lower }}_{{ data.define | to_snake | lower }}(const void *data, size_t length); +{% endif %} +{% endfor -%} +#define XAP_BROADCAST_TOKEN 0xFFFF + + +//////////////////////////////////////////////////////////////////////////////// +// Type definitions + +{% for name,data in xap.type_definitions | dictsort -%} +{% if data.type != 'struct' -%} +typedef {{ data.type | type_to_c('xap_'+(name|to_snake|lower)+'_t') }}; +{% endif -%} +{% endfor %} +{%- for name,data in xap.type_definitions | dictsort %} +{% if data.type == 'struct' -%} +typedef struct { +{%- for member in data.struct_members %} + {{ member.type | type_to_c(member.name) }}; +{%- endfor %} +} __attribute__((__packed__)) xap_{{ name | to_snake | lower }}_t{{ data.type | type_to_c_after }}; +_Static_assert(sizeof(xap_{{ name | to_snake | lower }}_t) == {{ data.struct_length }}, "xap_{{ name | to_snake | lower }}_t needs to be {{ data.struct_length }} bytes in size"); +{%- endif -%} +{% endfor %} + +//////////////////////////////////////////////////////////////////////////////// +// Route definitions + +{% macro export_route_types(prefix, container) -%} +{%- if 'routes' in container -%} +{% for route, data in container.routes | dictsort -%} +{%- set this_prefix_uc = (prefix + '_' + data.define) | upper -%} +{%- set this_prefix_lc = this_prefix_uc | lower -%} + +{% if 'request_struct_members' in data -%} +typedef struct { +{%- for member in data.request_struct_members %} + {{ member.type | type_to_c(member.name|lower) }}; +{%- endfor %} +} __attribute__((__packed__)) {{ this_prefix_lc | to_snake | lower }}_arg_t; +_Static_assert(sizeof({{ this_prefix_lc | to_snake | lower }}_arg_t) == {{ data.request_struct_length }}, "{{ this_prefix_lc | to_snake | lower }}_arg_t needs to be {{ data.request_struct_length }} bytes in size"); +{% elif 'request_type' in data -%} +typedef {{ data.request_type | type_to_c(this_prefix_lc+'_arg_t') }}; +{%- endif -%} + +{%- if 'return_struct_members' in data -%} +typedef struct { +{%- for member in data.return_struct_members %} + {{ member.type | type_to_c(member.name|lower) }}; +{%- endfor %} +} __attribute__((__packed__)) {{ this_prefix_lc | to_snake | lower }}_t; +_Static_assert(sizeof({{ this_prefix_lc | to_snake | lower }}_t) == {{ data.return_struct_length }}, "{{ this_prefix_lc | to_snake | lower }}_t needs to be {{ data.return_struct_length }} bytes in size"); +{%- elif 'return_type' in data -%} +{%- if '[' in data.return_type %} +typedef struct __attribute__((__packed__)) { {{ data.return_type | type_to_c('x') }}; } {{ this_prefix_lc }}_t; +{%- else -%} +typedef {{ data.return_type | type_to_c(this_prefix_lc+'_t') }}; +{%- endif -%} + +{%- endif %} +{{ export_route_types(this_prefix_lc, data) }} +{% endfor -%} +{%- endif -%} +{%- endmacro -%} + +{{ export_route_types('xap_route', xap) }} + +//////////////////////////////////////////////////////////////////////////////// +// Capabilities IDs + +{% macro export_route_ids(prefix, container) -%} +{%- if 'routes' in container -%} +{% for route, data in container.routes | dictsort -%} +{%- set this_prefix_uc = (prefix + '_' + data.define) | upper -%} +{%- set this_prefix_lc = this_prefix_uc | lower -%} +#define {{ this_prefix_uc }} {{ route }} +{{ export_route_ids(this_prefix_uc, data) }} +{%- endfor -%} +{%- endif -%} +{%- endmacro -%} + +{{ export_route_ids('XAP_ROUTE', xap) }} + +//////////////////////////////////////////////////////////////////////////////// +// Capabilities Masks + +{% macro export_route_masks(prefix, container, preprocessor_condition) -%} +{%- if 'routes' in container -%} +{% for route, data in container.routes | dictsort -%} +{%- set this_prefix_uc = (prefix + '_' + data.define) | upper -%} +{%- set this_prefix_lc = this_prefix_uc | lower -%} +{% if 'enable_if_preprocessor' in data %} +{% if preprocessor_condition == 'TRUE' %} +{%- set condition = "(" + data.enable_if_preprocessor + ")" -%} +{% else %} +{%- set condition = "(" + preprocessor_condition + " && (" + data.enable_if_preprocessor + "))" -%} +{% endif %} +{% else %} +{%- set condition = preprocessor_condition -%} +{% endif %} +{% if condition == 'TRUE' %} +#define {{ this_prefix_uc }}_MASK (UINT32_C(1) << ({{ this_prefix_uc }})) +{% else %} +#if ({{ condition }}) +#define {{ this_prefix_uc }}_MASK (UINT32_C(1) << ({{ this_prefix_uc }})) +#else // ({{ condition }}) +#define {{ this_prefix_uc }}_MASK 0 +#endif // ({{ condition }}) +{% endif %} +{{ export_route_masks(this_prefix_uc, data, condition) }} +{%- endfor -%} +{%- endif -%} +{%- endmacro -%} + +{{ export_route_masks('XAP_ROUTE', xap, 'TRUE') }} + +//////////////////////////////////////////////////////////////////////////////// +// Capabilities Values + +{% macro export_route_capabilities(prefix, container) -%} +{%- if 'routes' in container -%} +#define {{ prefix }}_CAPABILITIES (0 \ +{% for route, data in container.routes | dictsort -%} +{%- set this_prefix_uc = (prefix + '_' + data.define) | upper -%} + | ({{ this_prefix_uc }}_MASK) \ +{% endfor -%} + ) +{% for route, data in container.routes | dictsort -%} +{%- set this_prefix_uc = (prefix + '_' + data.define) | upper -%} +{{ export_route_capabilities(this_prefix_uc, data) }} +{% endfor -%} +{%- endif -%} +{%- endmacro -%} + +{{ export_route_capabilities('XAP_ROUTE', xap) }} diff --git a/data/templates/xap/firmware/xap_generated.inl.j2 b/data/templates/xap/firmware/xap_generated.inl.j2 new file mode 100755 index 000000000000..00da36fa5dd9 --- /dev/null +++ b/data/templates/xap/firmware/xap_generated.inl.j2 @@ -0,0 +1,9 @@ +{{ GPL2_HEADER_C_LIKE }} +{{ GENERATED_HEADER_C_LIKE }} + +//////////////////////////////////////////////////////////////////////////////// +// Full XAP {{ xap.version }} definitions + +#if 0 +{{ xap | tojson(4) }} +#endif diff --git a/lib/python/qmk/cli/xap/generate_qmk.py b/lib/python/qmk/cli/xap/generate_qmk.py index 5339166b4b18..62a0a217f8da 100755 --- a/lib/python/qmk/cli/xap/generate_qmk.py +++ b/lib/python/qmk/cli/xap/generate_qmk.py @@ -4,9 +4,9 @@ from qmk.path import normpath from qmk.keyboard import keyboard_completer, keyboard_folder +from qmk.xap.common import render_xap_output 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') @@ -28,6 +28,12 @@ def xap_generate_qmk_inc(cli): generate_inline(cli.args.output, cli.args.keyboard, cli.args.keymap) + with open(normpath(str(cli.args.output.resolve()) + '.generated.j2'), 'w', encoding='utf-8') as out_file: + r = render_xap_output('firmware', 'xap_generated.inl.j2', keyboard=cli.args.keyboard, keymap=cli.args.keymap) + while r.find('\n\n\n') != -1: + r = r.replace('\n\n\n', '\n\n') + out_file.write(r) + @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') @@ -46,7 +52,11 @@ def xap_generate_qmk_h(cli): cli.subcommands['xap-generate-qmk-h'].print_help() return False - generate_header(cli.args.output, cli.args.keyboard, cli.args.keymap) + with open(cli.args.output, 'w', encoding='utf-8') as out_file: + r = render_xap_output('firmware', 'xap_generated.h.j2', keyboard=cli.args.keyboard, keymap=cli.args.keymap) + while r.find('\n\n\n') != -1: + r = r.replace('\n\n\n', '\n\n') + out_file.write(r) @cli.argument('-o', '--output', type=normpath, help='File to write to') diff --git a/lib/python/qmk/xap/common.py b/lib/python/qmk/xap/common.py index dc3c53e02212..6a58f9eb3a83 100755 --- a/lib/python/qmk/xap/common.py +++ b/lib/python/qmk/xap/common.py @@ -9,11 +9,13 @@ from jinja2 import Environment, FileSystemLoader, select_autoescape from qmk.casing import to_snake -from qmk.constants import QMK_FIRMWARE +from qmk.constants import QMK_FIRMWARE, GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE +from qmk.git import git_get_version 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 +from qmk.xap.jinja2_filters import attach_filters XAP_SPEC = 'xap.hjson' @@ -57,16 +59,18 @@ def _get_jinja2_env(data_templates_xap_subdir: str): return j2 -def render_xap_output(data_templates_xap_subdir, file_to_render, defs): +def render_xap_output(data_templates_xap_subdir, file_to_render, defs=None, **kwargs): + if defs is None: + defs = latest_xap_defs() j2 = _get_jinja2_env(data_templates_xap_subdir) - j2.globals['to_snake'] = to_snake + attach_filters(j2) constants = {} for feature in ['rgblight', 'rgb_matrix', 'led_matrix']: constants[feature] = load_lighting_spec(feature) - return j2.get_template(file_to_render).render(xap=defs, xap_str=hjson.dumps(defs), constants=constants) + return j2.get_template(file_to_render).render(xap=defs, qmk_version=git_get_version(), xap_str=hjson.dumps(defs), constants=constants, GPL2_HEADER_C_LIKE=GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE=GENERATED_HEADER_C_LIKE, **kwargs) def _find_kb_spec(kb): diff --git a/lib/python/qmk/xap/gen_firmware/blob_generator.py b/lib/python/qmk/xap/gen_firmware/blob_generator.py index be137571d542..153cedfd8a9d 100644 --- a/lib/python/qmk/xap/gen_firmware/blob_generator.py +++ b/lib/python/qmk/xap/gen_firmware/blob_generator.py @@ -7,7 +7,7 @@ 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.json_encoders import InfoJSONEncoder from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE @@ -57,6 +57,12 @@ def generate_blob(output_file, keyboard, keymap): lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', ''] + lines.append(f'#if 0') + lines.append('// Blob contains a minified+gzipped version of the following:') + lines.append(json.dumps(info_json, cls=InfoJSONEncoder)) + lines.append(f'#endif') + lines.append('') + # Gen output file lines.append('static const unsigned char config_blob_gz[] PROGMEM = {') lines.append(data) diff --git a/lib/python/qmk/xap/gen_firmware/header_generator.py b/lib/python/qmk/xap/gen_firmware/header_generator.py deleted file mode 100755 index bb005e159801..000000000000 --- a/lib/python/qmk/xap/gen_firmware/header_generator.py +++ /dev/null @@ -1,268 +0,0 @@ -"""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)):02X}{int(b.group(2)):02X}{int(b.group(3)):04X}ul') - b = prog.findall(git_get_version() or "") or [('0', '0', '0')] - lines.append(f'#define QMK_BCD_VERSION 0x{int(b[0][0]):02X}{int(b[0][1]):02X}{int(b[0][2]):04X}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) diff --git a/lib/python/qmk/xap/jinja2_filters.py b/lib/python/qmk/xap/jinja2_filters.py new file mode 100644 index 000000000000..a525c3e374e6 --- /dev/null +++ b/lib/python/qmk/xap/jinja2_filters.py @@ -0,0 +1,70 @@ +"""This script enables attachment of XAP-specific filters to Jinja2 +""" +import re +from fnvhash import fnv1a_32 +from jinja2 import Environment + +from qmk.casing import to_snake + +TRIPLET_PATTERN = re.compile(r'^(\d+)\.(\d+)\.(\d+)') +TYPE_ARRAY_PATTERN = re.compile(r'^([^\[]+)\[([^[\]]+)\]$') + + +def _fnv1a_32(s: str): + res = fnv1a_32(bytes(s, 'utf-8')) + return f'0x{res:08X}' + + +def _xap_type_to_c_before(xt: str): + m = TYPE_ARRAY_PATTERN.match(xt) + if m: + return _xap_type_to_c(m.group(1)) + if xt == 'u8': + return 'uint8_t' + elif xt == 'u16': + return 'uint16_t' + elif xt == 'u32': + return 'uint32_t' + elif xt == 'u64': + return 'uint64_t' + elif xt == 'string': + return 'const char*' + elif xt == 'token': + return 'xap_token_t' + elif xt == 'response_flags': + return 'xap_response_flags_t' + raise TypeError(f'Unknown XAP type: {xt}') + + +def _xap_type_to_c_after(xt: str): + m = TYPE_ARRAY_PATTERN.match(xt) + if m: + try: + extent = int(m.group(2)) + return f'[{extent}]' + except: + return '[]' + return '' + + +def _xap_type_to_c(xt: str, name: str = None): + if name is not None: + name = re.sub(' ', '_', name) + return f'{(_xap_type_to_c_before(xt))} {name}{(_xap_type_to_c_after(xt))}' + return f'{(_xap_type_to_c_before(xt))}{(_xap_type_to_c_after(xt))}' + + +def _triplet_to_bcd(value: str): + m = TRIPLET_PATTERN.match(value) + if not m: + return '0' + return f'0x{int(m.group(1)):02d}{int(m.group(2)):02d}{int(m.group(3)):04d}' + + +def attach_filters(j2: Environment): + j2.filters['to_snake'] = to_snake + j2.filters['triplet_to_bcd'] = _triplet_to_bcd + j2.filters['fnv1a_32'] = _fnv1a_32 + j2.filters['type_to_c'] = _xap_type_to_c + j2.filters['type_to_c_before'] = _xap_type_to_c_before + j2.filters['type_to_c_after'] = _xap_type_to_c_after From 5083c618cfbffb7ce49ef9f02253bfa41b8443ba Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Sun, 16 Oct 2022 13:25:48 +1100 Subject: [PATCH 150/203] `qmk pytest` --- lib/python/qmk/xap/common.py | 1 - lib/python/qmk/xap/gen_firmware/blob_generator.py | 4 ++-- lib/python/qmk/xap/jinja2_filters.py | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/python/qmk/xap/common.py b/lib/python/qmk/xap/common.py index 6a58f9eb3a83..b964685e29ce 100755 --- a/lib/python/qmk/xap/common.py +++ b/lib/python/qmk/xap/common.py @@ -8,7 +8,6 @@ from typing import OrderedDict from jinja2 import Environment, FileSystemLoader, select_autoescape -from qmk.casing import to_snake from qmk.constants import QMK_FIRMWARE, GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE from qmk.git import git_get_version from qmk.json_schema import json_load, validate diff --git a/lib/python/qmk/xap/gen_firmware/blob_generator.py b/lib/python/qmk/xap/gen_firmware/blob_generator.py index 153cedfd8a9d..319b1f22ed78 100644 --- a/lib/python/qmk/xap/gen_firmware/blob_generator.py +++ b/lib/python/qmk/xap/gen_firmware/blob_generator.py @@ -57,10 +57,10 @@ def generate_blob(output_file, keyboard, keymap): lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', ''] - lines.append(f'#if 0') + lines.append('#if 0') lines.append('// Blob contains a minified+gzipped version of the following:') lines.append(json.dumps(info_json, cls=InfoJSONEncoder)) - lines.append(f'#endif') + lines.append('#endif') lines.append('') # Gen output file diff --git a/lib/python/qmk/xap/jinja2_filters.py b/lib/python/qmk/xap/jinja2_filters.py index a525c3e374e6..625747dd4814 100644 --- a/lib/python/qmk/xap/jinja2_filters.py +++ b/lib/python/qmk/xap/jinja2_filters.py @@ -42,7 +42,7 @@ def _xap_type_to_c_after(xt: str): try: extent = int(m.group(2)) return f'[{extent}]' - except: + except ValueError: return '[]' return '' From 9fcab705a99729526e8d013d3c2181c8931b557a Mon Sep 17 00:00:00 2001 From: zvecr Date: Sun, 16 Oct 2022 20:24:37 +0100 Subject: [PATCH 151/203] Align client/docs gen with recent changes --- .../xap/client/python/constants.py.j2 | 7 +- data/templates/xap/client/python/routes.py.j2 | 3 +- data/templates/xap/client/python/types.py.j2 | 2 + data/templates/xap/docs/docs.md.j2 | 2 + data/templates/xap/docs/route_request.md.j2 | 8 -- data/templates/xap/docs/route_response.md.j2 | 8 -- data/templates/xap/docs/routes.md.j2 | 27 ++++- data/templates/xap/docs/versions.md.j2 | 6 + .../templates/xap/firmware/xap_generated.h.j2 | 4 +- .../xap/firmware/xap_generated.inl.j2 | 4 +- docs/xap_0.0.1.md | 28 ++++- docs/xap_0.1.0.md | 58 +++++++--- docs/xap_0.2.0.md | 76 ++++++++----- docs/xap_0.3.0.md | 106 +++++++++++------- docs/xap_protocol.md | 27 ++++- lib/python/qmk/cli/xap/generate_docs.py | 17 ++- lib/python/qmk/cli/xap/generate_python.py | 11 +- lib/python/qmk/constants.py | 31 +++++ lib/python/qmk/xap/common.py | 12 +- lib/python/qmk/xap/jinja2_filters.py | 5 + lib/python/xap_client/constants.py | 1 + lib/python/xap_client/routes.py | 1 - 22 files changed, 308 insertions(+), 136 deletions(-) delete mode 100644 data/templates/xap/docs/route_request.md.j2 delete mode 100644 data/templates/xap/docs/route_response.md.j2 create mode 100644 data/templates/xap/docs/versions.md.j2 diff --git a/data/templates/xap/client/python/constants.py.j2 b/data/templates/xap/client/python/constants.py.j2 index 61c9767d9709..a972890dae6c 100644 --- a/data/templates/xap/client/python/constants.py.j2 +++ b/data/templates/xap/client/python/constants.py.j2 @@ -1,14 +1,17 @@ +{{ constants.GPL2_HEADER_SH_LIKE }} +{{ constants.GENERATED_HEADER_SH_LIKE }} from enum import IntEnum # version: 0.0.1 class RgblightModes(IntEnum): -{%- for id, effect in constants.rgblight.effects | dictsort %} +{%- for id, effect in specs.rgblight.effects | dictsort %} {{ effect.key }} = {{ id }} {%- endfor %} +# version: 0.0.1 class RgbMatrixModes(IntEnum): -{%- for id, effect in constants.rgb_matrix.effects | dictsort %} +{%- for id, effect in specs.rgb_matrix.effects | dictsort %} {{ effect.key }} = {{ id }} {%- endfor %} diff --git a/data/templates/xap/client/python/routes.py.j2 b/data/templates/xap/client/python/routes.py.j2 index 3cd65912e76a..475ee23137dd 100644 --- a/data/templates/xap/client/python/routes.py.j2 +++ b/data/templates/xap/client/python/routes.py.j2 @@ -1,4 +1,5 @@ - +{{ constants.GPL2_HEADER_SH_LIKE }} +{{ constants.GENERATED_HEADER_SH_LIKE }} class XAPRouteError(Exception): pass diff --git a/data/templates/xap/client/python/types.py.j2 b/data/templates/xap/client/python/types.py.j2 index ca9b90cdae55..0c49de3e133f 100644 --- a/data/templates/xap/client/python/types.py.j2 +++ b/data/templates/xap/client/python/types.py.j2 @@ -1,3 +1,5 @@ +{{ constants.GPL2_HEADER_SH_LIKE }} +{{ constants.GENERATED_HEADER_SH_LIKE }} from collections import namedtuple from enum import IntFlag, IntEnum from struct import Struct diff --git a/data/templates/xap/docs/docs.md.j2 b/data/templates/xap/docs/docs.md.j2 index 7d6225ff7b08..a43b50b8ce60 100644 --- a/data/templates/xap/docs/docs.md.j2 +++ b/data/templates/xap/docs/docs.md.j2 @@ -1,3 +1,5 @@ +{{ constants.GPL2_HEADER_XML_LIKE }} +{{ constants.GENERATED_HEADER_XML_LIKE }} {%- for item in xap.documentation.order -%} {%- if not item[0:1] == '!' -%} {{ xap.documentation.get(item) }} diff --git a/data/templates/xap/docs/route_request.md.j2 b/data/templates/xap/docs/route_request.md.j2 deleted file mode 100644 index 8d9c1fd37dbe..000000000000 --- a/data/templates/xap/docs/route_request.md.j2 +++ /dev/null @@ -1,8 +0,0 @@ -{%- if route.request_type == 'struct' -%} -__Request:__ -{%- for member in route.request_struct_members -%} -
{{ " "|safe*4 }}* {{ member.name }}: `{{ member.type }}` -{%- endfor -%} -{%- elif route.request_type -%} -__Request:__ `{{ route.request_type }}` -{%- endif -%} \ No newline at end of file diff --git a/data/templates/xap/docs/route_response.md.j2 b/data/templates/xap/docs/route_response.md.j2 deleted file mode 100644 index 64f4e065e08b..000000000000 --- a/data/templates/xap/docs/route_response.md.j2 +++ /dev/null @@ -1,8 +0,0 @@ -{%- if route.return_type == 'struct' -%} -__Response:__ -{%- for member in route.return_struct_members -%} -
{{ " "|safe*4 }}* {{ member.name }}: `{{ member.type }}` -{%- endfor -%} -{%- elif route.return_type -%} -__Response:__ `{{ route.return_type }}` -{%- endif -%} \ No newline at end of file diff --git a/data/templates/xap/docs/routes.md.j2 b/data/templates/xap/docs/routes.md.j2 index 4c6edee210fc..92afcad20ba7 100644 --- a/data/templates/xap/docs/routes.md.j2 +++ b/data/templates/xap/docs/routes.md.j2 @@ -1,3 +1,22 @@ +{%- macro gen_payload(name, type, members) -%} +{%- if type == 'struct' -%} +__{{ name }}:__ +{%- for member in members -%} +
{{ " "|safe*4 }}* {{ member.name }}: `{{ member.type }}` +{%- endfor -%} +{%- elif type -%} +__{{ name }}:__ `{{ type }}` +{%- endif -%} +{%- endmacro -%} + +{%- macro gen_payloads(route) -%} +{{ gen_payload('Request', route.request_type, route.request_struct_members) }}{%- if route.return_type and route.request_type -%}

{% endif %}{{ gen_payload('Response', route.return_type, route.return_struct_members) }} +{%- endmacro -%} + +{%- macro gen_tags(route) -%} +{% if 'secure' == route.permissions %}__Secure__{% endif %} +{%- endmacro -%} + {%- for id, route in xap.routes | dictsort %} ### {{ route.name }} - `{{ id }}` {{ route.description }} @@ -7,9 +26,7 @@ | -- | -- | -- | -- | -- | {%- for subid, subroute in route.routes | dictsort %} {%- if not subroute.routes %} -{%- with route=subroute %} -| {{ subroute.name }} | `{{ id }} {{ subid }}` | {% if 'secure' == subroute.permissions %}__Secure__{% endif %} | {%- include 'route_request.md.j2' -%}{%- if subroute.return_type and subroute.request_type -%}

{% endif %}{%- include 'route_response.md.j2' -%} | {{ subroute.description.replace('\n', '
') }}| -{%- endwith %} +| {{ subroute.name }} | `{{ id }} {{ subid }}` | {{ gen_tags(subroute) }} | {{ gen_payloads(subroute) }} | {{ subroute.description | newline_to_br }}| {%- endif %} {%- endfor %} @@ -23,9 +40,7 @@ | -- | -- | -- | -- | -- | {%- for subsubid, subsubroute in subroute.routes | dictsort %} {%- if not subsubroute.routes %} -{%- with route=subsubroute %} -| {{ subsubroute.name }} | `{{ id }} {{ subid }} {{ subsubid }}` | {% if 'secure' == subsubroute.permissions %}__Secure__{% endif %} | {%- include 'route_request.md.j2' -%}{%- if subsubroute.return_type and subsubroute.request_type -%}

{% endif %}{%- include 'route_response.md.j2' -%} | {{ subsubroute.description.replace('\n', '
') }}| -{%- endwith %} +| {{ subsubroute.name }} | `{{ id }} {{ subid }} {{ subsubid }}` | {{ gen_tags(subsubroute) }} | {{ gen_payloads(subsubroute) }} | {{ subsubroute.description | newline_to_br }}| {%- endif %} {%- endfor %} {%- endif %} diff --git a/data/templates/xap/docs/versions.md.j2 b/data/templates/xap/docs/versions.md.j2 new file mode 100644 index 000000000000..4d91913abe39 --- /dev/null +++ b/data/templates/xap/docs/versions.md.j2 @@ -0,0 +1,6 @@ +{{ constants.GPL2_HEADER_XML_LIKE }} +{{ constants.GENERATED_HEADER_XML_LIKE }} + +{%- for ver in versions | reverse -%} +* [XAP Version {{ ver }}](xap_{{ ver }}.md) +{% endfor %} diff --git a/data/templates/xap/firmware/xap_generated.h.j2 b/data/templates/xap/firmware/xap_generated.h.j2 index ef35bfacaa98..efb238bae3d1 100755 --- a/data/templates/xap/firmware/xap_generated.h.j2 +++ b/data/templates/xap/firmware/xap_generated.h.j2 @@ -1,5 +1,5 @@ -{{ GPL2_HEADER_C_LIKE }} -{{ GENERATED_HEADER_C_LIKE }} +{{ constants.GPL2_HEADER_C_LIKE }} +{{ constants.GENERATED_HEADER_C_LIKE }} #pragma once #include diff --git a/data/templates/xap/firmware/xap_generated.inl.j2 b/data/templates/xap/firmware/xap_generated.inl.j2 index 00da36fa5dd9..2f30ffcac727 100755 --- a/data/templates/xap/firmware/xap_generated.inl.j2 +++ b/data/templates/xap/firmware/xap_generated.inl.j2 @@ -1,5 +1,5 @@ -{{ GPL2_HEADER_C_LIKE }} -{{ GENERATED_HEADER_C_LIKE }} +{{ constants.GPL2_HEADER_C_LIKE }} +{{ constants.GENERATED_HEADER_C_LIKE }} //////////////////////////////////////////////////////////////////////////////// // Full XAP {{ xap.version }} definitions diff --git a/docs/xap_0.0.1.md b/docs/xap_0.0.1.md index 410a2d9d32d7..d94ada81d672 100644 --- a/docs/xap_0.0.1.md +++ b/docs/xap_0.0.1.md @@ -1,3 +1,29 @@ + + + + # QMK Firmware XAP Specs This document describes the requirements of the QMK XAP ("extensible application protocol") API. @@ -80,6 +106,6 @@ This subsystem is always present, and provides the ability to query information | Name | Route | Tags | Payloads | Description | | -- | -- | -- | -- | -- | -| Version Query | `0x00 0x00` | |__Response:__ `u32`| 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}.| +| Version Query | `0x00 0x00` | | __Response:__ `u32` | 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}.| diff --git a/docs/xap_0.1.0.md b/docs/xap_0.1.0.md index de34463df2f6..59bdd878d22f 100644 --- a/docs/xap_0.1.0.md +++ b/docs/xap_0.1.0.md @@ -1,3 +1,29 @@ + + + + # QMK Firmware XAP Specs This document describes the requirements of the QMK XAP ("extensible application protocol") API. @@ -91,12 +117,12 @@ This subsystem is always present, and provides the ability to query information | Name | Route | Tags | Payloads | Description | | -- | -- | -- | -- | -- | -| Version Query | `0x00 0x00` | |__Response:__ `u32`| 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}.| -| 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

* 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| -| Secure Unlock | `0x00 0x04` | || Initiate secure route unlock sequence| -| Secure Lock | `0x00 0x05` | || Disable secure routes| +| Version Query | `0x00 0x00` | | __Response:__ `u32` | 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}.| +| 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

* 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| +| 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. @@ -104,16 +130,16 @@ This subsystem is always present, and provides the ability to address QMK-specif | Name | Route | Tags | Payloads | Description | | -- | -- | -- | -- | -- | -| Version Query | `0x01 0x00` | |__Response:__ `u32`| 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}.| -| 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:__
    * Vendor ID: `u16`
    * Product ID: `u16`
    * Product Version: `u16`
    * 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:__ `u16`| Retrieves the length of the configuration data bundled within the firmware| -| Config Blob Chunk | `0x01 0x06` | |__Request:__ `u16`

__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

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| -| Hardware Identifier | `0x01 0x08` | |__Response:__ `u32[4]`| Retrieves a unique identifier for the board.| -| Reinitialize EEPROM | `0x01 0x09` | __Secure__ |__Response:__ `u8`| Reinitializes the keyboard's EEPROM (persistent memory)

May not be present - if QMK capabilities query returns “true”, then reinitialize is supported

* 0 means secure routes are disabled, and should be considered as a failure
* 1 means successful, board will reinitialize and then reboot| +| Version Query | `0x01 0x00` | | __Response:__ `u32` | 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}.| +| 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:__
    * Vendor ID: `u16`
    * Product ID: `u16`
    * Product Version: `u16`
    * 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:__ `u16` | Retrieves the length of the configuration data bundled within the firmware| +| Config Blob Chunk | `0x01 0x06` | | __Request:__ `u16`

__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

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| +| Hardware Identifier | `0x01 0x08` | | __Response:__ `u32[4]` | Retrieves a unique identifier for the board.| +| Reinitialize EEPROM | `0x01 0x09` | __Secure__ | __Response:__ `u8` | Reinitializes the keyboard's EEPROM (persistent memory)

May not be present - if QMK capabilities query returns “true”, then reinitialize is supported

* 0 means secure routes are disabled, and should be considered as a failure
* 1 means successful, board will reinitialize and then reboot| ### Keyboard - `0x02` This subsystem is always present, and reserved for vendor-specific functionality. No routes are defined by XAP. diff --git a/docs/xap_0.2.0.md b/docs/xap_0.2.0.md index cfdece98917e..4e640c68a66c 100644 --- a/docs/xap_0.2.0.md +++ b/docs/xap_0.2.0.md @@ -1,3 +1,29 @@ + + + + # QMK Firmware XAP Specs This document describes the requirements of the QMK XAP ("extensible application protocol") API. @@ -91,12 +117,12 @@ This subsystem is always present, and provides the ability to query information | Name | Route | Tags | Payloads | Description | | -- | -- | -- | -- | -- | -| Version Query | `0x00 0x00` | |__Response:__ `u32`| 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}.| -| 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

* 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| -| Secure Unlock | `0x00 0x04` | || Initiate secure route unlock sequence| -| Secure Lock | `0x00 0x05` | || Disable secure routes| +| Version Query | `0x00 0x00` | | __Response:__ `u32` | 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}.| +| 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

* 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| +| 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. @@ -104,16 +130,16 @@ This subsystem is always present, and provides the ability to address QMK-specif | Name | Route | Tags | Payloads | Description | | -- | -- | -- | -- | -- | -| Version Query | `0x01 0x00` | |__Response:__ `u32`| 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}.| -| 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:__
    * Vendor ID: `u16`
    * Product ID: `u16`
    * Product Version: `u16`
    * 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:__ `u16`| Retrieves the length of the configuration data bundled within the firmware| -| Config Blob Chunk | `0x01 0x06` | |__Request:__ `u16`

__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

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| -| Hardware Identifier | `0x01 0x08` | |__Response:__ `u32[4]`| Retrieves a unique identifier for the board.| -| Reinitialize EEPROM | `0x01 0x09` | __Secure__ |__Response:__ `u8`| Reinitializes the keyboard's EEPROM (persistent memory)

May not be present - if QMK capabilities query returns “true”, then reinitialize is supported

* 0 means secure routes are disabled, and should be considered as a failure
* 1 means successful, board will reinitialize and then reboot| +| Version Query | `0x01 0x00` | | __Response:__ `u32` | 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}.| +| 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:__
    * Vendor ID: `u16`
    * Product ID: `u16`
    * Product Version: `u16`
    * 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:__ `u16` | Retrieves the length of the configuration data bundled within the firmware| +| Config Blob Chunk | `0x01 0x06` | | __Request:__ `u16`

__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

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| +| Hardware Identifier | `0x01 0x08` | | __Response:__ `u32[4]` | Retrieves a unique identifier for the board.| +| Reinitialize EEPROM | `0x01 0x09` | __Secure__ | __Response:__ `u8` | Reinitializes the keyboard's EEPROM (persistent memory)

May not be present - if QMK capabilities query returns “true”, then reinitialize is supported

* 0 means secure routes are disabled, and should be considered as a failure
* 1 means successful, board will reinitialize and then reboot| ### Keyboard - `0x02` This subsystem is always present, and reserved for vendor-specific functionality. No routes are defined by XAP. @@ -129,10 +155,10 @@ 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:__
    * Layer: `u8`
    * Row: `u8`
    * Column: `u8`

__Response:__ `u16`| Query the Keycode at the requested location.| -| Get Encoder Keycode | `0x04 0x04` | |__Request:__
    * Layer: `u8`
    * Encoder: `u8`
    * Clockwise: `u8`

__Response:__ `u16`| Query the Keycode at the requested location.| +| 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:__
    * Layer: `u8`
    * Row: `u8`
    * Column: `u8`

__Response:__ `u16` | Query the Keycode at the requested location.| +| Get Encoder Keycode | `0x04 0x04` | | __Request:__
    * Layer: `u8`
    * Encoder: `u8`
    * Clockwise: `u8`

__Response:__ `u16` | Query the Keycode at the requested location.| ### Remapping - `0x05` This subsystem allows for live reassignment of keycodes without rebuilding the firmware. @@ -140,10 +166,10 @@ This subsystem allows for live reassignment of keycodes without rebuilding the f | 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:__
    * Layer: `u8`
    * Row: `u8`
    * Column: `u8`
    * Keycode: `u16`| Modify the Keycode at the requested location.| -| Set Encoder Keycode | `0x05 0x04` | __Secure__ |__Request:__
    * Layer: `u8`
    * Encoder: `u8`
    * Clockwise: `u8`
    * Keycode: `u16`| Modify the Keycode at the requested location.| +| 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:__
    * Layer: `u8`
    * Row: `u8`
    * Column: `u8`
    * Keycode: `u16` | Modify the Keycode at the requested location.| +| Set Encoder Keycode | `0x05 0x04` | __Secure__ | __Request:__
    * Layer: `u8`
    * Encoder: `u8`
    * Clockwise: `u8`
    * Keycode: `u16` | Modify the Keycode at the requested location.| ### Lighting - `0x06` This subsystem allows for control over the lighting subsystem. @@ -151,7 +177,7 @@ 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.| +| Capabilities Query | `0x06 0x01` | | __Response:__ `u32` | Lighting subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.| ## Broadcast messages diff --git a/docs/xap_0.3.0.md b/docs/xap_0.3.0.md index 8cc054859167..75dbf0e17674 100644 --- a/docs/xap_0.3.0.md +++ b/docs/xap_0.3.0.md @@ -1,3 +1,29 @@ + + + + # QMK Firmware XAP Specs This document describes the requirements of the QMK XAP ("extensible application protocol") API. @@ -91,12 +117,12 @@ This subsystem is always present, and provides the ability to query information | Name | Route | Tags | Payloads | Description | | -- | -- | -- | -- | -- | -| Version Query | `0x00 0x00` | |__Response:__ `u32`| 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}.| -| 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

* 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| -| Secure Unlock | `0x00 0x04` | || Initiate secure route unlock sequence| -| Secure Lock | `0x00 0x05` | || Disable secure routes| +| Version Query | `0x00 0x00` | | __Response:__ `u32` | 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}.| +| 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

* 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| +| 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. @@ -104,16 +130,16 @@ This subsystem is always present, and provides the ability to address QMK-specif | Name | Route | Tags | Payloads | Description | | -- | -- | -- | -- | -- | -| Version Query | `0x01 0x00` | |__Response:__ `u32`| 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}.| -| 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:__
    * Vendor ID: `u16`
    * Product ID: `u16`
    * Product Version: `u16`
    * 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:__ `u16`| Retrieves the length of the configuration data bundled within the firmware| -| Config Blob Chunk | `0x01 0x06` | |__Request:__ `u16`

__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

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| -| Hardware Identifier | `0x01 0x08` | |__Response:__ `u32[4]`| Retrieves a unique identifier for the board.| -| Reinitialize EEPROM | `0x01 0x09` | __Secure__ |__Response:__ `u8`| Reinitializes the keyboard's EEPROM (persistent memory)

May not be present - if QMK capabilities query returns “true”, then reinitialize is supported

* 0 means secure routes are disabled, and should be considered as a failure
* 1 means successful, board will reinitialize and then reboot| +| Version Query | `0x01 0x00` | | __Response:__ `u32` | 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}.| +| 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:__
    * Vendor ID: `u16`
    * Product ID: `u16`
    * Product Version: `u16`
    * 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:__ `u16` | Retrieves the length of the configuration data bundled within the firmware| +| Config Blob Chunk | `0x01 0x06` | | __Request:__ `u16`

__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

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| +| Hardware Identifier | `0x01 0x08` | | __Response:__ `u32[4]` | Retrieves a unique identifier for the board.| +| Reinitialize EEPROM | `0x01 0x09` | __Secure__ | __Response:__ `u8` | Reinitializes the keyboard's EEPROM (persistent memory)

May not be present - if QMK capabilities query returns “true”, then reinitialize is supported

* 0 means secure routes are disabled, and should be considered as a failure
* 1 means successful, board will reinitialize and then reboot| ### Keyboard - `0x02` This subsystem is always present, and reserved for vendor-specific functionality. No routes are defined by XAP. @@ -129,10 +155,10 @@ 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:__
    * Layer: `u8`
    * Row: `u8`
    * Column: `u8`

__Response:__ `u16`| Query the Keycode at the requested location.| -| Get Encoder Keycode | `0x04 0x04` | |__Request:__
    * Layer: `u8`
    * Encoder: `u8`
    * Clockwise: `u8`

__Response:__ `u16`| Query the Keycode at the requested location.| +| 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:__
    * Layer: `u8`
    * Row: `u8`
    * Column: `u8`

__Response:__ `u16` | Query the Keycode at the requested location.| +| Get Encoder Keycode | `0x04 0x04` | | __Request:__
    * Layer: `u8`
    * Encoder: `u8`
    * Clockwise: `u8`

__Response:__ `u16` | Query the Keycode at the requested location.| ### Remapping - `0x05` This subsystem allows for live reassignment of keycodes without rebuilding the firmware. @@ -140,10 +166,10 @@ This subsystem allows for live reassignment of keycodes without rebuilding the f | 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:__
    * Layer: `u8`
    * Row: `u8`
    * Column: `u8`
    * Keycode: `u16`| Modify the Keycode at the requested location.| -| Set Encoder Keycode | `0x05 0x04` | __Secure__ |__Request:__
    * Layer: `u8`
    * Encoder: `u8`
    * Clockwise: `u8`
    * Keycode: `u16`| Modify the Keycode at the requested location.| +| 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:__
    * Layer: `u8`
    * Row: `u8`
    * Column: `u8`
    * Keycode: `u16` | Modify the Keycode at the requested location.| +| Set Encoder Keycode | `0x05 0x04` | __Secure__ | __Request:__
    * Layer: `u8`
    * Encoder: `u8`
    * Clockwise: `u8`
    * Keycode: `u16` | Modify the Keycode at the requested location.| ### Lighting - `0x06` This subsystem allows for control over the lighting subsystem. @@ -151,40 +177,40 @@ 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.| +| Capabilities Query | `0x06 0x01` | | __Response:__ `u32` | Lighting subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.| #### backlight - `0x06 0x02` This subsystem allows for control over the backlight subsystem. | Name | Route | Tags | Payloads | Description | | -- | -- | -- | -- | -- | -| Capabilities Query | `0x06 0x02 0x01` | |__Response:__ `u32`| backlight subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.| -| Get Enabled Effects | `0x06 0x02 0x02` | |__Response:__ `u8`| Each bit should be considered as a "usable" effect id| -| Get Config | `0x06 0x02 0x03` | |__Response:__
    * enable: `u8`
    * mode: `u8`
    * val: `u8`| Query the current config.| -| Set Config | `0x06 0x02 0x04` | |__Request:__
    * enable: `u8`
    * mode: `u8`
    * val: `u8`| Set the current config.| -| Save Config | `0x06 0x02 0x05` | || Save the current config.| +| Capabilities Query | `0x06 0x02 0x01` | | __Response:__ `u32` | backlight subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.| +| Get Enabled Effects | `0x06 0x02 0x02` | | __Response:__ `u8` | Each bit should be considered as a "usable" effect id| +| Get Config | `0x06 0x02 0x03` | | __Response:__
    * enable: `u8`
    * mode: `u8`
    * val: `u8` | Query the current config.| +| Set Config | `0x06 0x02 0x04` | | __Request:__
    * enable: `u8`
    * mode: `u8`
    * val: `u8` | Set the current config.| +| Save Config | `0x06 0x02 0x05` | | | Save the current config.| #### rgblight - `0x06 0x03` This subsystem allows for control over the rgblight subsystem. | Name | Route | Tags | Payloads | Description | | -- | -- | -- | -- | -- | -| Capabilities Query | `0x06 0x03 0x01` | |__Response:__ `u32`| rgblight subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.| -| Get Enabled Effects | `0x06 0x03 0x02` | |__Response:__ `u64`| Each bit should be considered as a "usable" effect id| -| Get Config | `0x06 0x03 0x03` | |__Response:__
    * enable: `u8`
    * mode: `u8`
    * hue: `u8`
    * sat: `u8`
    * val: `u8`
    * speed: `u8`| Query the current config.| -| Set Config | `0x06 0x03 0x04` | |__Request:__
    * enable: `u8`
    * mode: `u8`
    * hue: `u8`
    * sat: `u8`
    * val: `u8`
    * speed: `u8`| Set the current config.| -| Save Config | `0x06 0x03 0x05` | || Save the current config.| +| Capabilities Query | `0x06 0x03 0x01` | | __Response:__ `u32` | rgblight subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.| +| Get Enabled Effects | `0x06 0x03 0x02` | | __Response:__ `u64` | Each bit should be considered as a "usable" effect id| +| Get Config | `0x06 0x03 0x03` | | __Response:__
    * enable: `u8`
    * mode: `u8`
    * hue: `u8`
    * sat: `u8`
    * val: `u8`
    * speed: `u8` | Query the current config.| +| Set Config | `0x06 0x03 0x04` | | __Request:__
    * enable: `u8`
    * mode: `u8`
    * hue: `u8`
    * sat: `u8`
    * val: `u8`
    * speed: `u8` | Set the current config.| +| Save Config | `0x06 0x03 0x05` | | | Save the current config.| #### rgbmatrix - `0x06 0x04` This subsystem allows for control over the rgb matrix subsystem. | Name | Route | Tags | Payloads | Description | | -- | -- | -- | -- | -- | -| Capabilities Query | `0x06 0x04 0x01` | |__Response:__ `u32`| rgb matrix subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.| -| Get Enabled Effects | `0x06 0x04 0x02` | |__Response:__ `u64`| Each bit should be considered as a "usable" effect id| -| Get Config | `0x06 0x04 0x03` | |__Response:__
    * enable: `u8`
    * mode: `u8`
    * hue: `u8`
    * sat: `u8`
    * val: `u8`
    * speed: `u8`
    * flags: `u8`| Query the current config.| -| Set Config | `0x06 0x04 0x04` | |__Request:__
    * enable: `u8`
    * mode: `u8`
    * hue: `u8`
    * sat: `u8`
    * val: `u8`
    * speed: `u8`
    * flags: `u8`| Set the current config.| -| Save Config | `0x06 0x04 0x05` | || Save the current config.| +| Capabilities Query | `0x06 0x04 0x01` | | __Response:__ `u32` | rgb matrix subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.| +| Get Enabled Effects | `0x06 0x04 0x02` | | __Response:__ `u64` | Each bit should be considered as a "usable" effect id| +| Get Config | `0x06 0x04 0x03` | | __Response:__
    * enable: `u8`
    * mode: `u8`
    * hue: `u8`
    * sat: `u8`
    * val: `u8`
    * speed: `u8`
    * flags: `u8` | Query the current config.| +| Set Config | `0x06 0x04 0x04` | | __Request:__
    * enable: `u8`
    * mode: `u8`
    * hue: `u8`
    * sat: `u8`
    * val: `u8`
    * speed: `u8`
    * flags: `u8` | Set the current config.| +| Save Config | `0x06 0x04 0x05` | | | Save the current config.| ## Broadcast messages diff --git a/docs/xap_protocol.md b/docs/xap_protocol.md index f70f605bb5b2..17638ff01cda 100644 --- a/docs/xap_protocol.md +++ b/docs/xap_protocol.md @@ -1,4 +1,29 @@ - + + + + * [XAP Version 0.3.0](xap_0.3.0.md) * [XAP Version 0.2.0](xap_0.2.0.md) * [XAP Version 0.1.0](xap_0.1.0.md) diff --git a/lib/python/qmk/cli/xap/generate_docs.py b/lib/python/qmk/cli/xap/generate_docs.py index ae1c9907b4a0..38fb9d2c2700 100755 --- a/lib/python/qmk/cli/xap/generate_docs.py +++ b/lib/python/qmk/cli/xap/generate_docs.py @@ -1,16 +1,18 @@ """This script generates the XAP protocol documentation. """ import hjson + +from milc import cli + 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 = [] + versions = [] overall = None for file in get_xap_definition_files(): @@ -22,17 +24,12 @@ def xap_generate_docs(cli): overall['response_flags']['bits'][str(n)] = {'name': '', 'description': '', 'define': '-'} output_doc = QMK_FIRMWARE / "docs" / f"{file.stem}.md" - docs_list.append(output_doc) + versions.append(overall['version']) 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" + output = render_xap_output('docs', 'versions.md.j2', overall, versions=versions) with open(output_doc, "w", encoding='utf-8') as out_file: - out_file.write('''\ - -''') - - for file in reversed(sorted(docs_list)): - ver = file.stem[4:] - out_file.write(f'* [XAP Version {ver}]({file.name})\n') + out_file.write(output) diff --git a/lib/python/qmk/cli/xap/generate_python.py b/lib/python/qmk/cli/xap/generate_python.py index cd7573cca10b..23fb7a81046b 100644 --- a/lib/python/qmk/cli/xap/generate_python.py +++ b/lib/python/qmk/cli/xap/generate_python.py @@ -1,11 +1,10 @@ """This script generates the python XAP client. """ -from qmk.constants import QMK_FIRMWARE, GPL2_HEADER_SH_LIKE, GENERATED_HEADER_SH_LIKE +from milc import cli -from qmk.xap.common import latest_xap_defs, render_xap_output from qmk.commands import dump_lines - -from milc import cli +from qmk.constants import QMK_FIRMWARE +from qmk.xap.common import latest_xap_defs, render_xap_output @cli.subcommand('Generates the python XAP client.', hidden=False if cli.config.user.developer else True) @@ -14,9 +13,7 @@ def xap_generate_python(cli): parent = QMK_FIRMWARE / 'lib' / 'python' / 'xap_client' for name in ['types.py', 'routes.py', 'constants.py']: - lines = [GPL2_HEADER_SH_LIKE, GENERATED_HEADER_SH_LIKE] - output = render_xap_output('client/python', f'{name}.j2', defs) - lines += output.split('\n') + lines = output.split('\n') dump_lines(parent / name, lines) diff --git a/lib/python/qmk/constants.py b/lib/python/qmk/constants.py index 8a13029a8a8f..191e6906f330 100644 --- a/lib/python/qmk/constants.py +++ b/lib/python/qmk/constants.py @@ -136,6 +136,11 @@ # SPDX-License-Identifier: GPL-2.0-or-later ''' +GPL2_HEADER_XML_LIKE = f'''\ + + +''' + GENERATED_HEADER_C_LIKE = '''\ /******************************************************************************* 88888888888 888 d8b .d888 d8b 888 d8b @@ -186,3 +191,29 @@ # ################################################################################ ''' + +GENERATED_HEADER_XML_LIKE = '''\ + +''' diff --git a/lib/python/qmk/xap/common.py b/lib/python/qmk/xap/common.py index b964685e29ce..fcbb4be43006 100755 --- a/lib/python/qmk/xap/common.py +++ b/lib/python/qmk/xap/common.py @@ -8,7 +8,7 @@ from typing import OrderedDict from jinja2 import Environment, FileSystemLoader, select_autoescape -from qmk.constants import QMK_FIRMWARE, GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE +import qmk.constants from qmk.git import git_get_version from qmk.json_schema import json_load, validate from qmk.decorators import lru_cache @@ -53,7 +53,7 @@ def load_lighting_spec(feature, version='latest'): def _get_jinja2_env(data_templates_xap_subdir: str): - templates_dir = os.path.join(QMK_FIRMWARE, 'data', 'templates', 'xap', data_templates_xap_subdir) + templates_dir = os.path.join(qmk.constants.QMK_FIRMWARE, 'data', 'templates', 'xap', data_templates_xap_subdir) j2 = Environment(loader=FileSystemLoader(templates_dir), autoescape=select_autoescape()) return j2 @@ -65,11 +65,11 @@ def render_xap_output(data_templates_xap_subdir, file_to_render, defs=None, **kw attach_filters(j2) - constants = {} + specs = {} for feature in ['rgblight', 'rgb_matrix', 'led_matrix']: - constants[feature] = load_lighting_spec(feature) + specs[feature] = load_lighting_spec(feature) - return j2.get_template(file_to_render).render(xap=defs, qmk_version=git_get_version(), xap_str=hjson.dumps(defs), constants=constants, GPL2_HEADER_C_LIKE=GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE=GENERATED_HEADER_C_LIKE, **kwargs) + return j2.get_template(file_to_render).render(xap=defs, qmk_version=git_get_version(), xap_str=hjson.dumps(defs), specs=specs, constants=qmk.constants, **kwargs) def _find_kb_spec(kb): @@ -130,7 +130,7 @@ def add_entry(target, k, v): def get_xap_definition_files(): """Get the sorted list of XAP definition files, from /data/xap. """ - xap_defs = QMK_FIRMWARE / "data" / "xap" + xap_defs = qmk.constants.QMK_FIRMWARE / "data" / "xap" return list(sorted(xap_defs.glob('**/xap_*.hjson'))) diff --git a/lib/python/qmk/xap/jinja2_filters.py b/lib/python/qmk/xap/jinja2_filters.py index 625747dd4814..c5b2aba55fd1 100644 --- a/lib/python/qmk/xap/jinja2_filters.py +++ b/lib/python/qmk/xap/jinja2_filters.py @@ -61,8 +61,13 @@ def _triplet_to_bcd(value: str): return f'0x{int(m.group(1)):02d}{int(m.group(2)):02d}{int(m.group(3)):04d}' +def _newline_to_br(value: str): + return value.replace('\n', '
') + + def attach_filters(j2: Environment): j2.filters['to_snake'] = to_snake + j2.filters['newline_to_br'] = _newline_to_br j2.filters['triplet_to_bcd'] = _triplet_to_bcd j2.filters['fnv1a_32'] = _fnv1a_32 j2.filters['type_to_c'] = _xap_type_to_c diff --git a/lib/python/xap_client/constants.py b/lib/python/xap_client/constants.py index 09d69e69861a..f78b13569649 100644 --- a/lib/python/xap_client/constants.py +++ b/lib/python/xap_client/constants.py @@ -75,6 +75,7 @@ class RgblightModes(IntEnum): TWINKLE_6 = 0x29 +# version: 0.0.1 class RgbMatrixModes(IntEnum): SOLID_COLOR = 0x00 ALPHAS_MODS = 0x01 diff --git a/lib/python/xap_client/routes.py b/lib/python/xap_client/routes.py index cace9b1591ac..658c486d0410 100644 --- a/lib/python/xap_client/routes.py +++ b/lib/python/xap_client/routes.py @@ -26,7 +26,6 @@ # ################################################################################ - class XAPRouteError(Exception): pass From edc14b29be318970dbe37106d2be947978da7c88 Mon Sep 17 00:00:00 2001 From: zvecr Date: Sun, 16 Oct 2022 23:55:40 +0100 Subject: [PATCH 152/203] Dump lighting maps out of inline gen --- lib/python/qmk/cli/__init__.py | 1 + lib/python/qmk/cli/xap/generate_lighting.py | 128 ++ .../qmk/xap/gen_firmware/inline_generator.py | 115 +- quantum/xap/lighting_map.h | 1092 +++++++++++++++++ quantum/xap/xap.c | 1 + 5 files changed, 1223 insertions(+), 114 deletions(-) create mode 100644 lib/python/qmk/cli/xap/generate_lighting.py create mode 100644 quantum/xap/lighting_map.h diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py index 191eda2f6429..23bfe2b84161 100644 --- a/lib/python/qmk/cli/__init__.py +++ b/lib/python/qmk/cli/__init__.py @@ -81,6 +81,7 @@ 'qmk.cli.xap', 'qmk.cli.xap.generate_docs', 'qmk.cli.xap.generate_json', + 'qmk.cli.xap.generate_lighting', 'qmk.cli.xap.generate_python', 'qmk.cli.xap.generate_qmk', ] diff --git a/lib/python/qmk/cli/xap/generate_lighting.py b/lib/python/qmk/cli/xap/generate_lighting.py new file mode 100644 index 000000000000..1716056489c9 --- /dev/null +++ b/lib/python/qmk/cli/xap/generate_lighting.py @@ -0,0 +1,128 @@ +from milc import cli + +from qmk.path import normpath +from qmk.commands import dump_lines +from qmk.xap.common import load_lighting_spec +from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE + +PREFIX_MAP = { + 'rgblight': { + 'ifdef': 'RGBLIGHT_EFFECT', + 'def': 'RGBLIGHT_MODE', + }, + 'rgb_matrix': { + 'ifdef': 'ENABLE_RGB_MATRIX', + 'def': 'RGB_MATRIX', + }, + 'led_matrix': { + 'ifdef': 'ENABLE_LED_MATRIX', + 'def': 'LED_MATRIX', + }, +} + + +def _append_lighting_map(lines, feature, spec): + """TODO: + """ + groups = spec.get('groups', {}) + ifdef_prefix = PREFIX_MAP[feature]['ifdef'] + def_prefix = PREFIX_MAP[feature]['def'] + + lines.append(f'static uint8_t {feature}_effect_map[][2] = {{') + for id, obj in spec.get('effects', {}).items(): + define = obj['define'] + offset = f' + {obj["offset"]}' if obj['offset'] else '' + + line = f''' +#ifdef {ifdef_prefix}_{define} + {{ {id}, {def_prefix}_{define}{offset}}}, +#endif''' + + group = groups.get(obj.get('group', None), {}).get('define', None) + if group: + line = f''' +#ifdef {group} +{line} +#endif''' + + lines.append(line) + + lines.append('};') + + # add helper funcs + lines.append( + f''' +uint8_t {feature}2xap(uint8_t val) {{ + for(uint8_t i = 0; i < ARRAY_SIZE({feature}_effect_map); i++) {{ + if ({feature}_effect_map[i][1] == val) + return {feature}_effect_map[i][0]; + }} + return 0xFF; +}} + +uint8_t xap2{feature}(uint8_t val) {{ + for(uint8_t i = 0; i < ARRAY_SIZE({feature}_effect_map); i++) {{ + if ({feature}_effect_map[i][0] == val) + return {feature}_effect_map[i][1]; + }} + return 0xFF; +}}''' + ) + + +def _append_lighting_bitmask(lines, feature, spec): + """TODO: + """ + groups = spec.get('groups', {}) + ifdef_prefix = PREFIX_MAP[feature]['ifdef'] + + lines.append(f'enum {{ ENABLED_{feature.upper()}_EFFECTS = 0') + for id, obj in spec.get('effects', {}).items(): + define = obj['define'] + + line = f''' +#ifdef {ifdef_prefix}_{define} + | (1ULL << {id}) +#endif''' + + group = groups.get(obj.get('group', None), {}).get('define', None) + if group: + line = f''' +#ifdef {group} +{line} +#endif''' + + lines.append(line) + + lines.append('};') + + +def _append_lighting_mapping(lines): + """TODO: + """ + # TODO: remove bodge for always enabled effects + lines.append(''' +#define RGBLIGHT_EFFECT_STATIC_LIGHT +#define ENABLE_RGB_MATRIX_SOLID_COLOR +#define ENABLE_LED_MATRIX_SOLID +''') + + for feature in PREFIX_MAP.keys(): + spec = load_lighting_spec(feature) + + lines.append(f'#ifdef {feature.upper()}_ENABLE') + _append_lighting_map(lines, feature, spec) + _append_lighting_bitmask(lines, feature, spec) + lines.append(f'#endif //{feature.upper()}_ENABLE') + + +@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.subcommand('Generates effect header.') +def xap_generate_lighting_map(cli): + # Preamble + lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', '// clang-format off'] + + _append_lighting_mapping(lines) + + dump_lines(cli.args.output, lines, cli.args.quiet) diff --git a/lib/python/qmk/xap/gen_firmware/inline_generator.py b/lib/python/qmk/xap/gen_firmware/inline_generator.py index 78ceb2aa1d3f..1dd72233a843 100755 --- a/lib/python/qmk/xap/gen_firmware/inline_generator.py +++ b/lib/python/qmk/xap/gen_firmware/inline_generator.py @@ -3,22 +3,7 @@ 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, load_lighting_spec - -PREFIX_MAP = { - 'rgblight': { - 'ifdef': 'RGBLIGHT_EFFECT', - 'def': 'RGBLIGHT_MODE', - }, - 'rgb_matrix': { - 'ifdef': 'ENABLE_RGB_MATRIX', - 'def': 'RGB_MATRIX', - }, - 'led_matrix': { - 'ifdef': 'ENABLE_LED_MATRIX', - 'def': 'LED_MATRIX', - }, -} +from qmk.xap.common import merge_xap_defs, route_conditions def _get_c_type(xap_type): @@ -298,101 +283,6 @@ def _append_broadcast_messages(lines, container): lines.append(f'void {name}(const void *data, size_t length){{ xap_broadcast({key}, data, length); }}') -def _append_lighting_map(lines, feature, spec): - """TODO: - """ - groups = spec.get('groups', {}) - ifdef_prefix = PREFIX_MAP[feature]['ifdef'] - def_prefix = PREFIX_MAP[feature]['def'] - - lines.append(f'static uint8_t {feature}_effect_map[][2] = {{') - for id, obj in spec.get('effects', {}).items(): - define = obj['define'] - offset = f' + {obj["offset"]}' if obj['offset'] else '' - - line = f''' -#ifdef {ifdef_prefix}_{define} - {{ {id}, {def_prefix}_{define}{offset}}}, -#endif''' - - group = groups.get(obj.get('group', None), {}).get('define', None) - if group: - line = f''' -#ifdef {group} -{line} -#endif''' - - lines.append(line) - - lines.append('};') - - # add helper funcs - lines.append( - f''' -uint8_t {feature}2xap(uint8_t val) {{ - for(uint8_t i = 0; i < ARRAY_SIZE({feature}_effect_map); i++) {{ - if ({feature}_effect_map[i][1] == val) - return {feature}_effect_map[i][0]; - }} - return 0xFF; -}} - -uint8_t xap2{feature}(uint8_t val) {{ - for(uint8_t i = 0; i < ARRAY_SIZE({feature}_effect_map); i++) {{ - if ({feature}_effect_map[i][0] == val) - return {feature}_effect_map[i][1]; - }} - return 0xFF; -}}''' - ) - - -def _append_lighting_bitmask(lines, feature, spec): - """TODO: - """ - groups = spec.get('groups', {}) - ifdef_prefix = PREFIX_MAP[feature]['ifdef'] - - lines.append(f'enum {{ ENABLED_{feature.upper()}_EFFECTS = 0') - for id, obj in spec.get('effects', {}).items(): - define = obj['define'] - - line = f''' -#ifdef {ifdef_prefix}_{define} - | (1ULL << {id}) -#endif''' - - group = groups.get(obj.get('group', None), {}).get('define', None) - if group: - line = f''' -#ifdef {group} -{line} -#endif''' - - lines.append(line) - - lines.append('};') - - -def _append_lighting_mapping(lines, xap_defs): - """TODO: - """ - # TODO: remove bodge for always enabled effects - lines.append(''' -#define RGBLIGHT_EFFECT_STATIC_LIGHT -#define ENABLE_RGB_MATRIX_SOLID_COLOR -#define ENABLE_LED_MATRIX_SOLID -''') - - for feature in PREFIX_MAP.keys(): - spec = load_lighting_spec(feature) - - lines.append(f'#ifdef {feature.upper()}_ENABLE') - _append_lighting_map(lines, feature, spec) - _append_lighting_bitmask(lines, feature, spec) - lines.append(f'#endif //{feature.upper()}_ENABLE') - - def generate_inline(output_file, keyboard, keymap): """Generates the XAP protocol header file, generated during normal build. """ @@ -401,9 +291,6 @@ def generate_inline(output_file, keyboard, keymap): # Preamble lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, ''] - # TODO: gen somewhere else? - _append_lighting_mapping(lines, xap_defs) - # Add all the generated code _append_broadcast_messages(lines, xap_defs) _append_routing_tables(lines, xap_defs) diff --git a/quantum/xap/lighting_map.h b/quantum/xap/lighting_map.h new file mode 100644 index 000000000000..420cb7e2b994 --- /dev/null +++ b/quantum/xap/lighting_map.h @@ -0,0 +1,1092 @@ +// Copyright 2022 QMK +// SPDX-License-Identifier: GPL-2.0-or-later + +/******************************************************************************* + 88888888888 888 d8b .d888 d8b 888 d8b + 888 888 Y8P d88P" Y8P 888 Y8P + 888 888 888 888 + 888 88888b. 888 .d8888b 888888 888 888 .d88b. 888 .d8888b + 888 888 "88b 888 88K 888 888 888 d8P Y8b 888 88K + 888 888 888 888 "Y8888b. 888 888 888 88888888 888 "Y8888b. + 888 888 888 888 X88 888 888 888 Y8b. 888 X88 + 888 888 888 888 88888P' 888 888 888 "Y8888 888 88888P' + 888 888 + 888 888 + 888 888 + .d88b. .d88b. 88888b. .d88b. 888d888 8888b. 888888 .d88b. .d88888 + d88P"88b d8P Y8b 888 "88b d8P Y8b 888P" "88b 888 d8P Y8b d88" 888 + 888 888 88888888 888 888 88888888 888 .d888888 888 88888888 888 888 + Y88b 888 Y8b. 888 888 Y8b. 888 888 888 Y88b. Y8b. Y88b 888 + "Y88888 "Y8888 888 888 "Y8888 888 "Y888888 "Y888 "Y8888 "Y88888 + 888 + Y8b d88P + "Y88P" +*******************************************************************************/ + +#pragma once +// clang-format off + +#define RGBLIGHT_EFFECT_STATIC_LIGHT +#define ENABLE_RGB_MATRIX_SOLID_COLOR +#define ENABLE_LED_MATRIX_SOLID + +#ifdef RGBLIGHT_ENABLE +static uint8_t rgblight_effect_map[][2] = { + +#ifdef RGBLIGHT_EFFECT_STATIC_LIGHT + { 0x00, RGBLIGHT_MODE_STATIC_LIGHT}, +#endif + +#ifdef RGBLIGHT_EFFECT_BREATHING + { 0x01, RGBLIGHT_MODE_BREATHING}, +#endif + +#ifdef RGBLIGHT_EFFECT_BREATHING + { 0x02, RGBLIGHT_MODE_BREATHING + 1}, +#endif + +#ifdef RGBLIGHT_EFFECT_BREATHING + { 0x03, RGBLIGHT_MODE_BREATHING + 2}, +#endif + +#ifdef RGBLIGHT_EFFECT_BREATHING + { 0x04, RGBLIGHT_MODE_BREATHING + 3}, +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD + { 0x05, RGBLIGHT_MODE_RAINBOW_MOOD}, +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD + { 0x06, RGBLIGHT_MODE_RAINBOW_MOOD + 1}, +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD + { 0x07, RGBLIGHT_MODE_RAINBOW_MOOD + 2}, +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL + { 0x08, RGBLIGHT_MODE_RAINBOW_SWIRL}, +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL + { 0x09, RGBLIGHT_MODE_RAINBOW_SWIRL + 1}, +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL + { 0x0A, RGBLIGHT_MODE_RAINBOW_SWIRL + 2}, +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL + { 0x0B, RGBLIGHT_MODE_RAINBOW_SWIRL + 3}, +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL + { 0x0C, RGBLIGHT_MODE_RAINBOW_SWIRL + 4}, +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL + { 0x0D, RGBLIGHT_MODE_RAINBOW_SWIRL + 5}, +#endif + +#ifdef RGBLIGHT_EFFECT_SNAKE + { 0x0E, RGBLIGHT_MODE_SNAKE}, +#endif + +#ifdef RGBLIGHT_EFFECT_SNAKE + { 0x0F, RGBLIGHT_MODE_SNAKE + 1}, +#endif + +#ifdef RGBLIGHT_EFFECT_SNAKE + { 0x10, RGBLIGHT_MODE_SNAKE + 2}, +#endif + +#ifdef RGBLIGHT_EFFECT_SNAKE + { 0x11, RGBLIGHT_MODE_SNAKE + 3}, +#endif + +#ifdef RGBLIGHT_EFFECT_SNAKE + { 0x12, RGBLIGHT_MODE_SNAKE + 4}, +#endif + +#ifdef RGBLIGHT_EFFECT_SNAKE + { 0x13, RGBLIGHT_MODE_SNAKE + 5}, +#endif + +#ifdef RGBLIGHT_EFFECT_KNIGHT + { 0x14, RGBLIGHT_MODE_KNIGHT}, +#endif + +#ifdef RGBLIGHT_EFFECT_KNIGHT + { 0x15, RGBLIGHT_MODE_KNIGHT + 1}, +#endif + +#ifdef RGBLIGHT_EFFECT_KNIGHT + { 0x16, RGBLIGHT_MODE_KNIGHT + 2}, +#endif + +#ifdef RGBLIGHT_EFFECT_CHRISTMAS + { 0x17, RGBLIGHT_MODE_CHRISTMAS}, +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + { 0x18, RGBLIGHT_MODE_STATIC_GRADIENT}, +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + { 0x19, RGBLIGHT_MODE_STATIC_GRADIENT + 1}, +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + { 0x1A, RGBLIGHT_MODE_STATIC_GRADIENT + 2}, +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + { 0x1B, RGBLIGHT_MODE_STATIC_GRADIENT + 3}, +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + { 0x1C, RGBLIGHT_MODE_STATIC_GRADIENT + 4}, +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + { 0x1D, RGBLIGHT_MODE_STATIC_GRADIENT + 5}, +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + { 0x1E, RGBLIGHT_MODE_STATIC_GRADIENT + 6}, +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + { 0x1F, RGBLIGHT_MODE_STATIC_GRADIENT + 7}, +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + { 0x20, RGBLIGHT_MODE_STATIC_GRADIENT + 8}, +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + { 0x21, RGBLIGHT_MODE_STATIC_GRADIENT + 9}, +#endif + +#ifdef RGBLIGHT_EFFECT_RGB_TEST + { 0x22, RGBLIGHT_MODE_RGB_TEST}, +#endif + +#ifdef RGBLIGHT_EFFECT_ALTERNATING + { 0x23, RGBLIGHT_MODE_ALTERNATING}, +#endif + +#ifdef RGBLIGHT_EFFECT_TWINKLE + { 0x24, RGBLIGHT_MODE_TWINKLE}, +#endif + +#ifdef RGBLIGHT_EFFECT_TWINKLE + { 0x25, RGBLIGHT_MODE_TWINKLE + 1}, +#endif + +#ifdef RGBLIGHT_EFFECT_TWINKLE + { 0x26, RGBLIGHT_MODE_TWINKLE + 2}, +#endif + +#ifdef RGBLIGHT_EFFECT_TWINKLE + { 0x27, RGBLIGHT_MODE_TWINKLE + 3}, +#endif + +#ifdef RGBLIGHT_EFFECT_TWINKLE + { 0x28, RGBLIGHT_MODE_TWINKLE + 4}, +#endif + +#ifdef RGBLIGHT_EFFECT_TWINKLE + { 0x29, RGBLIGHT_MODE_TWINKLE + 5}, +#endif +}; + +uint8_t rgblight2xap(uint8_t val) { + for(uint8_t i = 0; i < ARRAY_SIZE(rgblight_effect_map); i++) { + if (rgblight_effect_map[i][1] == val) + return rgblight_effect_map[i][0]; + } + return 0xFF; +} + +uint8_t xap2rgblight(uint8_t val) { + for(uint8_t i = 0; i < ARRAY_SIZE(rgblight_effect_map); i++) { + if (rgblight_effect_map[i][0] == val) + return rgblight_effect_map[i][1]; + } + return 0xFF; +} +enum { ENABLED_RGBLIGHT_EFFECTS = 0 + +#ifdef RGBLIGHT_EFFECT_STATIC_LIGHT + | (1ULL << 0x00) +#endif + +#ifdef RGBLIGHT_EFFECT_BREATHING + | (1ULL << 0x01) +#endif + +#ifdef RGBLIGHT_EFFECT_BREATHING + | (1ULL << 0x02) +#endif + +#ifdef RGBLIGHT_EFFECT_BREATHING + | (1ULL << 0x03) +#endif + +#ifdef RGBLIGHT_EFFECT_BREATHING + | (1ULL << 0x04) +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD + | (1ULL << 0x05) +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD + | (1ULL << 0x06) +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD + | (1ULL << 0x07) +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL + | (1ULL << 0x08) +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL + | (1ULL << 0x09) +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL + | (1ULL << 0x0A) +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL + | (1ULL << 0x0B) +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL + | (1ULL << 0x0C) +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL + | (1ULL << 0x0D) +#endif + +#ifdef RGBLIGHT_EFFECT_SNAKE + | (1ULL << 0x0E) +#endif + +#ifdef RGBLIGHT_EFFECT_SNAKE + | (1ULL << 0x0F) +#endif + +#ifdef RGBLIGHT_EFFECT_SNAKE + | (1ULL << 0x10) +#endif + +#ifdef RGBLIGHT_EFFECT_SNAKE + | (1ULL << 0x11) +#endif + +#ifdef RGBLIGHT_EFFECT_SNAKE + | (1ULL << 0x12) +#endif + +#ifdef RGBLIGHT_EFFECT_SNAKE + | (1ULL << 0x13) +#endif + +#ifdef RGBLIGHT_EFFECT_KNIGHT + | (1ULL << 0x14) +#endif + +#ifdef RGBLIGHT_EFFECT_KNIGHT + | (1ULL << 0x15) +#endif + +#ifdef RGBLIGHT_EFFECT_KNIGHT + | (1ULL << 0x16) +#endif + +#ifdef RGBLIGHT_EFFECT_CHRISTMAS + | (1ULL << 0x17) +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + | (1ULL << 0x18) +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + | (1ULL << 0x19) +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + | (1ULL << 0x1A) +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + | (1ULL << 0x1B) +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + | (1ULL << 0x1C) +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + | (1ULL << 0x1D) +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + | (1ULL << 0x1E) +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + | (1ULL << 0x1F) +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + | (1ULL << 0x20) +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + | (1ULL << 0x21) +#endif + +#ifdef RGBLIGHT_EFFECT_RGB_TEST + | (1ULL << 0x22) +#endif + +#ifdef RGBLIGHT_EFFECT_ALTERNATING + | (1ULL << 0x23) +#endif + +#ifdef RGBLIGHT_EFFECT_TWINKLE + | (1ULL << 0x24) +#endif + +#ifdef RGBLIGHT_EFFECT_TWINKLE + | (1ULL << 0x25) +#endif + +#ifdef RGBLIGHT_EFFECT_TWINKLE + | (1ULL << 0x26) +#endif + +#ifdef RGBLIGHT_EFFECT_TWINKLE + | (1ULL << 0x27) +#endif + +#ifdef RGBLIGHT_EFFECT_TWINKLE + | (1ULL << 0x28) +#endif + +#ifdef RGBLIGHT_EFFECT_TWINKLE + | (1ULL << 0x29) +#endif +}; +#endif //RGBLIGHT_ENABLE +#ifdef RGB_MATRIX_ENABLE +static uint8_t rgb_matrix_effect_map[][2] = { + +#ifdef ENABLE_RGB_MATRIX_SOLID_COLOR + { 0x00, RGB_MATRIX_SOLID_COLOR}, +#endif + +#ifdef ENABLE_RGB_MATRIX_ALPHAS_MODS + { 0x01, RGB_MATRIX_ALPHAS_MODS}, +#endif + +#ifdef ENABLE_RGB_MATRIX_GRADIENT_UP_DOWN + { 0x02, RGB_MATRIX_GRADIENT_UP_DOWN}, +#endif + +#ifdef ENABLE_RGB_MATRIX_GRADIENT_LEFT_RIGHT + { 0x03, RGB_MATRIX_GRADIENT_LEFT_RIGHT}, +#endif + +#ifdef ENABLE_RGB_MATRIX_BREATHING + { 0x04, RGB_MATRIX_BREATHING}, +#endif + +#ifdef ENABLE_RGB_MATRIX_BAND_SAT + { 0x05, RGB_MATRIX_BAND_SAT}, +#endif + +#ifdef ENABLE_RGB_MATRIX_BAND_VAL + { 0x06, RGB_MATRIX_BAND_VAL}, +#endif + +#ifdef ENABLE_RGB_MATRIX_BAND_PINWHEEL_SAT + { 0x07, RGB_MATRIX_BAND_PINWHEEL_SAT}, +#endif + +#ifdef ENABLE_RGB_MATRIX_BAND_PINWHEEL_VAL + { 0x08, RGB_MATRIX_BAND_PINWHEEL_VAL}, +#endif + +#ifdef ENABLE_RGB_MATRIX_BAND_SPIRAL_SAT + { 0x09, RGB_MATRIX_BAND_SPIRAL_SAT}, +#endif + +#ifdef ENABLE_RGB_MATRIX_BAND_SPIRAL_VAL + { 0x0A, RGB_MATRIX_BAND_SPIRAL_VAL}, +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_ALL + { 0x0B, RGB_MATRIX_CYCLE_ALL}, +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT + { 0x0C, RGB_MATRIX_CYCLE_LEFT_RIGHT}, +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_UP_DOWN + { 0x0D, RGB_MATRIX_CYCLE_UP_DOWN}, +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_OUT_IN + { 0x0E, RGB_MATRIX_CYCLE_OUT_IN}, +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_OUT_IN_DUAL + { 0x0F, RGB_MATRIX_CYCLE_OUT_IN_DUAL}, +#endif + +#ifdef ENABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON + { 0x10, RGB_MATRIX_RAINBOW_MOVING_CHEVRON}, +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_PINWHEEL + { 0x11, RGB_MATRIX_CYCLE_PINWHEEL}, +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_SPIRAL + { 0x12, RGB_MATRIX_CYCLE_SPIRAL}, +#endif + +#ifdef ENABLE_RGB_MATRIX_DUAL_BEACON + { 0x13, RGB_MATRIX_DUAL_BEACON}, +#endif + +#ifdef ENABLE_RGB_MATRIX_RAINBOW_BEACON + { 0x14, RGB_MATRIX_RAINBOW_BEACON}, +#endif + +#ifdef ENABLE_RGB_MATRIX_RAINBOW_PINWHEELS + { 0x15, RGB_MATRIX_RAINBOW_PINWHEELS}, +#endif + +#ifdef ENABLE_RGB_MATRIX_RAINDROPS + { 0x16, RGB_MATRIX_RAINDROPS}, +#endif + +#ifdef ENABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS + { 0x17, RGB_MATRIX_JELLYBEAN_RAINDROPS}, +#endif + +#ifdef ENABLE_RGB_MATRIX_HUE_BREATHING + { 0x18, RGB_MATRIX_HUE_BREATHING}, +#endif + +#ifdef ENABLE_RGB_MATRIX_HUE_PENDULUM + { 0x19, RGB_MATRIX_HUE_PENDULUM}, +#endif + +#ifdef ENABLE_RGB_MATRIX_HUE_WAVE + { 0x1A, RGB_MATRIX_HUE_WAVE}, +#endif + +#ifdef ENABLE_RGB_MATRIX_PIXEL_FRACTAL + { 0x1B, RGB_MATRIX_PIXEL_FRACTAL}, +#endif + +#ifdef ENABLE_RGB_MATRIX_PIXEL_FLOW + { 0x1C, RGB_MATRIX_PIXEL_FLOW}, +#endif + +#ifdef ENABLE_RGB_MATRIX_PIXEL_RAIN + { 0x1D, RGB_MATRIX_PIXEL_RAIN}, +#endif + +#ifdef RGB_MATRIX_FRAMEBUFFER_EFFECTS + +#ifdef ENABLE_RGB_MATRIX_TYPING_HEATMAP + { 0x1E, RGB_MATRIX_TYPING_HEATMAP}, +#endif +#endif + +#ifdef RGB_MATRIX_FRAMEBUFFER_EFFECTS + +#ifdef ENABLE_RGB_MATRIX_DIGITAL_RAIN + { 0x1F, RGB_MATRIX_DIGITAL_RAIN}, +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE + { 0x20, RGB_MATRIX_SOLID_REACTIVE_SIMPLE}, +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE + { 0x21, RGB_MATRIX_SOLID_REACTIVE}, +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_WIDE + { 0x22, RGB_MATRIX_SOLID_REACTIVE_WIDE}, +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE + { 0x23, RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE}, +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_CROSS + { 0x24, RGB_MATRIX_SOLID_REACTIVE_CROSS}, +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTICROSS + { 0x25, RGB_MATRIX_SOLID_REACTIVE_MULTICROSS}, +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_NEXUS + { 0x26, RGB_MATRIX_SOLID_REACTIVE_NEXUS}, +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS + { 0x27, RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS}, +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SPLASH + { 0x28, RGB_MATRIX_SPLASH}, +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_MULTISPLASH + { 0x29, RGB_MATRIX_MULTISPLASH}, +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_SPLASH + { 0x2A, RGB_MATRIX_SOLID_SPLASH}, +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_MULTISPLASH + { 0x2B, RGB_MATRIX_SOLID_MULTISPLASH}, +#endif +#endif +}; + +uint8_t rgb_matrix2xap(uint8_t val) { + for(uint8_t i = 0; i < ARRAY_SIZE(rgb_matrix_effect_map); i++) { + if (rgb_matrix_effect_map[i][1] == val) + return rgb_matrix_effect_map[i][0]; + } + return 0xFF; +} + +uint8_t xap2rgb_matrix(uint8_t val) { + for(uint8_t i = 0; i < ARRAY_SIZE(rgb_matrix_effect_map); i++) { + if (rgb_matrix_effect_map[i][0] == val) + return rgb_matrix_effect_map[i][1]; + } + return 0xFF; +} +enum { ENABLED_RGB_MATRIX_EFFECTS = 0 + +#ifdef ENABLE_RGB_MATRIX_SOLID_COLOR + | (1ULL << 0x00) +#endif + +#ifdef ENABLE_RGB_MATRIX_ALPHAS_MODS + | (1ULL << 0x01) +#endif + +#ifdef ENABLE_RGB_MATRIX_GRADIENT_UP_DOWN + | (1ULL << 0x02) +#endif + +#ifdef ENABLE_RGB_MATRIX_GRADIENT_LEFT_RIGHT + | (1ULL << 0x03) +#endif + +#ifdef ENABLE_RGB_MATRIX_BREATHING + | (1ULL << 0x04) +#endif + +#ifdef ENABLE_RGB_MATRIX_BAND_SAT + | (1ULL << 0x05) +#endif + +#ifdef ENABLE_RGB_MATRIX_BAND_VAL + | (1ULL << 0x06) +#endif + +#ifdef ENABLE_RGB_MATRIX_BAND_PINWHEEL_SAT + | (1ULL << 0x07) +#endif + +#ifdef ENABLE_RGB_MATRIX_BAND_PINWHEEL_VAL + | (1ULL << 0x08) +#endif + +#ifdef ENABLE_RGB_MATRIX_BAND_SPIRAL_SAT + | (1ULL << 0x09) +#endif + +#ifdef ENABLE_RGB_MATRIX_BAND_SPIRAL_VAL + | (1ULL << 0x0A) +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_ALL + | (1ULL << 0x0B) +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT + | (1ULL << 0x0C) +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_UP_DOWN + | (1ULL << 0x0D) +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_OUT_IN + | (1ULL << 0x0E) +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_OUT_IN_DUAL + | (1ULL << 0x0F) +#endif + +#ifdef ENABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON + | (1ULL << 0x10) +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_PINWHEEL + | (1ULL << 0x11) +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_SPIRAL + | (1ULL << 0x12) +#endif + +#ifdef ENABLE_RGB_MATRIX_DUAL_BEACON + | (1ULL << 0x13) +#endif + +#ifdef ENABLE_RGB_MATRIX_RAINBOW_BEACON + | (1ULL << 0x14) +#endif + +#ifdef ENABLE_RGB_MATRIX_RAINBOW_PINWHEELS + | (1ULL << 0x15) +#endif + +#ifdef ENABLE_RGB_MATRIX_RAINDROPS + | (1ULL << 0x16) +#endif + +#ifdef ENABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS + | (1ULL << 0x17) +#endif + +#ifdef ENABLE_RGB_MATRIX_HUE_BREATHING + | (1ULL << 0x18) +#endif + +#ifdef ENABLE_RGB_MATRIX_HUE_PENDULUM + | (1ULL << 0x19) +#endif + +#ifdef ENABLE_RGB_MATRIX_HUE_WAVE + | (1ULL << 0x1A) +#endif + +#ifdef ENABLE_RGB_MATRIX_PIXEL_FRACTAL + | (1ULL << 0x1B) +#endif + +#ifdef ENABLE_RGB_MATRIX_PIXEL_FLOW + | (1ULL << 0x1C) +#endif + +#ifdef ENABLE_RGB_MATRIX_PIXEL_RAIN + | (1ULL << 0x1D) +#endif + +#ifdef RGB_MATRIX_FRAMEBUFFER_EFFECTS + +#ifdef ENABLE_RGB_MATRIX_TYPING_HEATMAP + | (1ULL << 0x1E) +#endif +#endif + +#ifdef RGB_MATRIX_FRAMEBUFFER_EFFECTS + +#ifdef ENABLE_RGB_MATRIX_DIGITAL_RAIN + | (1ULL << 0x1F) +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE + | (1ULL << 0x20) +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE + | (1ULL << 0x21) +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_WIDE + | (1ULL << 0x22) +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE + | (1ULL << 0x23) +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_CROSS + | (1ULL << 0x24) +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTICROSS + | (1ULL << 0x25) +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_NEXUS + | (1ULL << 0x26) +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS + | (1ULL << 0x27) +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SPLASH + | (1ULL << 0x28) +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_MULTISPLASH + | (1ULL << 0x29) +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_SPLASH + | (1ULL << 0x2A) +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_MULTISPLASH + | (1ULL << 0x2B) +#endif +#endif +}; +#endif //RGB_MATRIX_ENABLE +#ifdef LED_MATRIX_ENABLE +static uint8_t led_matrix_effect_map[][2] = { + +#ifdef ENABLE_LED_MATRIX_SOLID + { 0x00, LED_MATRIX_SOLID}, +#endif + +#ifdef ENABLE_LED_MATRIX_ALPHAS_MODS + { 0x01, LED_MATRIX_ALPHAS_MODS}, +#endif + +#ifdef ENABLE_LED_MATRIX_BREATHING + { 0x02, LED_MATRIX_BREATHING}, +#endif + +#ifdef ENABLE_LED_MATRIX_BAND + { 0x03, LED_MATRIX_BAND}, +#endif + +#ifdef ENABLE_LED_MATRIX_BAND_PINWHEEL + { 0x04, LED_MATRIX_BAND_PINWHEEL}, +#endif + +#ifdef ENABLE_LED_MATRIX_BAND_SPIRAL + { 0x05, LED_MATRIX_BAND_SPIRAL}, +#endif + +#ifdef ENABLE_LED_MATRIX_CYCLE_LEFT_RIGHT + { 0x06, LED_MATRIX_CYCLE_LEFT_RIGHT}, +#endif + +#ifdef ENABLE_LED_MATRIX_CYCLE_UP_DOWN + { 0x07, LED_MATRIX_CYCLE_UP_DOWN}, +#endif + +#ifdef ENABLE_LED_MATRIX_CYCLE_OUT_IN + { 0x08, LED_MATRIX_CYCLE_OUT_IN}, +#endif + +#ifdef ENABLE_LED_MATRIX_DUAL_BEACON + { 0x09, LED_MATRIX_DUAL_BEACON}, +#endif + +#ifdef ENABLE_LED_MATRIX_WAVE_LEFT_RIGHT + { 0x0A, LED_MATRIX_WAVE_LEFT_RIGHT}, +#endif + +#ifdef ENABLE_LED_MATRIX_WAVE_UP_DOWN + { 0x0B, LED_MATRIX_WAVE_UP_DOWN}, +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE + { 0x0C, LED_MATRIX_SOLID_REACTIVE_SIMPLE}, +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE + { 0x0D, LED_MATRIX_SOLID_REACTIVE_WIDE}, +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE + { 0x0E, LED_MATRIX_SOLID_REACTIVE_MULTIWIDE}, +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_CROSS + { 0x0F, LED_MATRIX_SOLID_REACTIVE_CROSS}, +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS + { 0x10, LED_MATRIX_SOLID_REACTIVE_MULTICROSS}, +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS + { 0x11, LED_MATRIX_SOLID_REACTIVE_NEXUS}, +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS + { 0x12, LED_MATRIX_SOLID_REACTIVE_MULTINEXUS}, +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_SPLASH + { 0x13, LED_MATRIX_SOLID_SPLASH}, +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_MULTISPLASH + { 0x14, LED_MATRIX_SOLID_MULTISPLASH}, +#endif +#endif +}; + +uint8_t led_matrix2xap(uint8_t val) { + for(uint8_t i = 0; i < ARRAY_SIZE(led_matrix_effect_map); i++) { + if (led_matrix_effect_map[i][1] == val) + return led_matrix_effect_map[i][0]; + } + return 0xFF; +} + +uint8_t xap2led_matrix(uint8_t val) { + for(uint8_t i = 0; i < ARRAY_SIZE(led_matrix_effect_map); i++) { + if (led_matrix_effect_map[i][0] == val) + return led_matrix_effect_map[i][1]; + } + return 0xFF; +} +enum { ENABLED_LED_MATRIX_EFFECTS = 0 + +#ifdef ENABLE_LED_MATRIX_SOLID + | (1ULL << 0x00) +#endif + +#ifdef ENABLE_LED_MATRIX_ALPHAS_MODS + | (1ULL << 0x01) +#endif + +#ifdef ENABLE_LED_MATRIX_BREATHING + | (1ULL << 0x02) +#endif + +#ifdef ENABLE_LED_MATRIX_BAND + | (1ULL << 0x03) +#endif + +#ifdef ENABLE_LED_MATRIX_BAND_PINWHEEL + | (1ULL << 0x04) +#endif + +#ifdef ENABLE_LED_MATRIX_BAND_SPIRAL + | (1ULL << 0x05) +#endif + +#ifdef ENABLE_LED_MATRIX_CYCLE_LEFT_RIGHT + | (1ULL << 0x06) +#endif + +#ifdef ENABLE_LED_MATRIX_CYCLE_UP_DOWN + | (1ULL << 0x07) +#endif + +#ifdef ENABLE_LED_MATRIX_CYCLE_OUT_IN + | (1ULL << 0x08) +#endif + +#ifdef ENABLE_LED_MATRIX_DUAL_BEACON + | (1ULL << 0x09) +#endif + +#ifdef ENABLE_LED_MATRIX_WAVE_LEFT_RIGHT + | (1ULL << 0x0A) +#endif + +#ifdef ENABLE_LED_MATRIX_WAVE_UP_DOWN + | (1ULL << 0x0B) +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE + | (1ULL << 0x0C) +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE + | (1ULL << 0x0D) +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE + | (1ULL << 0x0E) +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_CROSS + | (1ULL << 0x0F) +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS + | (1ULL << 0x10) +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS + | (1ULL << 0x11) +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS + | (1ULL << 0x12) +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_SPLASH + | (1ULL << 0x13) +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_MULTISPLASH + | (1ULL << 0x14) +#endif +#endif +}; +#endif //LED_MATRIX_ENABLE diff --git a/quantum/xap/xap.c b/quantum/xap/xap.c index 62e4590ac265..6222a091dc03 100644 --- a/quantum/xap/xap.c +++ b/quantum/xap/xap.c @@ -18,6 +18,7 @@ #include #include "secure.h" +#include "lighting_map.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) { From bc56585932b3d657c927ced6f66562e24d7bfcf3 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Mon, 17 Oct 2022 10:16:30 +1100 Subject: [PATCH 153/203] Actually take into account keyboard/keymap xap specs --- lib/python/qmk/cli/xap/generate_qmk.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/python/qmk/cli/xap/generate_qmk.py b/lib/python/qmk/cli/xap/generate_qmk.py index 62a0a217f8da..fd6ef52b97eb 100755 --- a/lib/python/qmk/cli/xap/generate_qmk.py +++ b/lib/python/qmk/cli/xap/generate_qmk.py @@ -4,7 +4,7 @@ from qmk.path import normpath from qmk.keyboard import keyboard_completer, keyboard_folder -from qmk.xap.common import render_xap_output +from qmk.xap.common import render_xap_output, merge_xap_defs from qmk.xap.gen_firmware.blob_generator import generate_blob from qmk.xap.gen_firmware.inline_generator import generate_inline @@ -28,8 +28,9 @@ def xap_generate_qmk_inc(cli): generate_inline(cli.args.output, cli.args.keyboard, cli.args.keymap) + defs = merge_xap_defs(cli.args.keyboard, cli.args.keymap) with open(normpath(str(cli.args.output.resolve()) + '.generated.j2'), 'w', encoding='utf-8') as out_file: - r = render_xap_output('firmware', 'xap_generated.inl.j2', keyboard=cli.args.keyboard, keymap=cli.args.keymap) + r = render_xap_output('firmware', 'xap_generated.inl.j2', defs, keyboard=cli.args.keyboard, keymap=cli.args.keymap) while r.find('\n\n\n') != -1: r = r.replace('\n\n\n', '\n\n') out_file.write(r) @@ -52,8 +53,9 @@ def xap_generate_qmk_h(cli): cli.subcommands['xap-generate-qmk-h'].print_help() return False + defs = merge_xap_defs(cli.args.keyboard, cli.args.keymap) with open(cli.args.output, 'w', encoding='utf-8') as out_file: - r = render_xap_output('firmware', 'xap_generated.h.j2', keyboard=cli.args.keyboard, keymap=cli.args.keymap) + r = render_xap_output('firmware', 'xap_generated.h.j2', defs, keyboard=cli.args.keyboard, keymap=cli.args.keymap) while r.find('\n\n\n') != -1: r = r.replace('\n\n\n', '\n\n') out_file.write(r) From d9c8a64c18ea33ad01392fa0fe7bb0d44e5ad605 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Thu, 27 Oct 2022 11:54:51 +1100 Subject: [PATCH 154/203] Fix build error with NKRO_ENABLE=no --- quantum/xap/xap_handlers.c | 1 + 1 file changed, 1 insertion(+) diff --git a/quantum/xap/xap_handlers.c b/quantum/xap/xap_handlers.c index 565dddc4cd06..6937e83b5270 100644 --- a/quantum/xap/xap_handlers.c +++ b/quantum/xap/xap_handlers.c @@ -14,6 +14,7 @@ * along with this program. If not, see . */ +#include #include #include From 3ee042f236c4a88b088ac063c701e57ca625001a Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 27 Oct 2022 11:22:08 +0100 Subject: [PATCH 155/203] set_rgb_matrix_config shouldnt touch eeprom --- quantum/xap/xap_handlers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quantum/xap/xap_handlers.c b/quantum/xap/xap_handlers.c index 6937e83b5270..a16dbe34c5d6 100644 --- a/quantum/xap/xap_handlers.c +++ b/quantum/xap/xap_handlers.c @@ -338,7 +338,7 @@ bool xap_respond_set_rgb_matrix_config(xap_token_t token, const void *data, size rgb_matrix_mode_noeeprom(mode); rgb_matrix_sethsv_noeeprom(arg->hue, arg->sat, arg->val); rgb_matrix_set_speed_noeeprom(arg->speed); - rgb_matrix_set_flags(arg->flags); + rgb_matrix_set_flags_noeeprom(arg->flags); xap_respond_success(token); return true; From d84090ec96f9583e4e7491f7d75e36f02be4d222 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Tue, 1 Nov 2022 15:16:25 +1100 Subject: [PATCH 156/203] Bad Merge --- quantum/eeconfig.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quantum/eeconfig.h b/quantum/eeconfig.h index 1927fb3d096e..95b416883ede 100644 --- a/quantum/eeconfig.h +++ b/quantum/eeconfig.h @@ -29,7 +29,7 @@ along with this program. If not, see . #define EECONFIG_MAGIC (uint16_t *)0 #define EECONFIG_DEBUG (uint8_t *)2 #define EECONFIG_DEFAULT_LAYER (uint8_t *)3 -#define EECONFIG_KEYMAP (uint8_t *)4 +#define EECONFIG_KEYMAP (uint16_t *)4 #define EECONFIG_MOUSEKEY_ACCEL (uint8_t *)5 #define EECONFIG_BACKLIGHT (uint8_t *)6 #define EECONFIG_AUDIO (uint8_t *)7 From 1983421b7d68f0e74c89f17b741b04c8a015efda Mon Sep 17 00:00:00 2001 From: Stefan Kerkmann Date: Tue, 1 Nov 2022 16:54:56 +0100 Subject: [PATCH 157/203] XAP: prevent OOB reads in config blob handler (#18926) This fixes two bugs: 1. An invalid offset could be specified which wasn't checked to be in the bounds of the config blob. 2. The data_len check was incorrect as it would allow reading one byte past the config blob lenght. Before the changes the following operation wouldn't fail: Assuming we have blob of 64 bytes size and attempt a read with an offset of 32 and data_len of 32, we actually try to read 32 bytes starting from the 33. byte in the config blob. This reads exactly one byte past array. Therefore we have to subtract one byte the get the correct length. --- quantum/xap/xap.c | 8 ++++++-- quantum/xap/xap_handlers.c | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/quantum/xap/xap.c b/quantum/xap/xap.c index 6222a091dc03..26f896b75ef4 100644 --- a/quantum/xap/xap.c +++ b/quantum/xap/xap.c @@ -21,8 +21,12 @@ #include "lighting_map.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; + if (offset >= CONFIG_BLOB_GZ_LEN) { + return false; + } + + if (offset + data_len >= CONFIG_BLOB_GZ_LEN) { + data_len = (CONFIG_BLOB_GZ_LEN - 1) - offset; } memcpy_P(data, &config_blob_gz[offset], data_len); diff --git a/quantum/xap/xap_handlers.c b/quantum/xap/xap_handlers.c index a16dbe34c5d6..9277be00b75e 100644 --- a/quantum/xap/xap_handlers.c +++ b/quantum/xap/xap_handlers.c @@ -71,7 +71,9 @@ bool xap_respond_get_config_blob_chunk(xap_token_t token, const void *data, size 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)); + if (!get_config_blob_chunk(offset, (uint8_t *)&ret, sizeof(ret))) { + return false; + } return xap_respond_data(token, &ret, sizeof(ret)); } From 0af00fb45c3ad394db7402f0beb6af04591d9b4b Mon Sep 17 00:00:00 2001 From: zvecr Date: Mon, 7 Nov 2022 23:30:09 +0000 Subject: [PATCH 158/203] Stub some keycode rendering --- data/constants/keycodes_0.0.1.hjson | 1510 --------------------------- lib/python/qmk/cli/xap/xap.py | 22 +- 2 files changed, 21 insertions(+), 1511 deletions(-) delete mode 100644 data/constants/keycodes_0.0.1.hjson diff --git a/data/constants/keycodes_0.0.1.hjson b/data/constants/keycodes_0.0.1.hjson deleted file mode 100644 index 72bed1ab2332..000000000000 --- a/data/constants/keycodes_0.0.1.hjson +++ /dev/null @@ -1,1510 +0,0 @@ -{ - "groups": { - "basic": {}, - "media": {}, - "modifiers": {}, - "mouse": {} - - }, - "ranges": { - "0x0000/0x00FF": {} - }, - "keycodes": { - "0x0000": { - "group": "basic", - "key": "KC_NO", - "label": "", - "aliases": [ - "XXXXXXX" - ] - }, - "0x0001": { - "group": "basic", - "key": "KC_TRANSPARENT", - "label": "", - "aliases": [ - "_______", - "KC_TRNS" - ] - }, - "0x0004": { - "group": "basic", - "key": "KC_A", - "label": "A" - }, - "0x0005": { - "group": "basic", - "key": "KC_B", - "label": "B" - }, - "0x0006": { - "group": "basic", - "key": "KC_C", - "label": "C" - }, - "0x0007": { - "group": "basic", - "key": "KC_D", - "label": "D" - }, - "0x0008": { - "group": "basic", - "key": "KC_E", - "label": "E" - }, - "0x0009": { - "group": "basic", - "key": "KC_F", - "label": "F" - }, - "0x000a": { - "group": "basic", - "key": "KC_G", - "label": "G" - }, - "0x000b": { - "group": "basic", - "key": "KC_H", - "label": "H" - }, - "0x000c": { - "group": "basic", - "key": "KC_I", - "label": "I" - }, - "0x000d": { - "group": "basic", - "key": "KC_J", - "label": "J" - }, - "0x000e": { - "group": "basic", - "key": "KC_K", - "label": "K" - }, - "0x000f": { - "group": "basic", - "key": "KC_L", - "label": "L" - }, - "0x0010": { - "group": "basic", - "key": "KC_M", - "label": "M" - }, - "0x0011": { - "group": "basic", - "key": "KC_N", - "label": "N" - }, - "0x0012": { - "group": "basic", - "key": "KC_O", - "label": "O" - }, - "0x0013": { - "group": "basic", - "key": "KC_P", - "label": "P" - }, - "0x0014": { - "group": "basic", - "key": "KC_Q", - "label": "Q" - }, - "0x0015": { - "group": "basic", - "key": "KC_R", - "label": "R" - }, - "0x0016": { - "group": "basic", - "key": "KC_S", - "label": "S" - }, - "0x0017": { - "group": "basic", - "key": "KC_T", - "label": "T" - }, - "0x0018": { - "group": "basic", - "key": "KC_U", - "label": "U" - }, - "0x0019": { - "group": "basic", - "key": "KC_V", - "label": "V" - }, - "0x001a": { - "group": "basic", - "key": "KC_W", - "label": "W" - }, - "0x001b": { - "group": "basic", - "key": "KC_X", - "label": "X" - }, - "0x001c": { - "group": "basic", - "key": "KC_Y", - "label": "Y" - }, - "0x001d": { - "group": "basic", - "key": "KC_Z", - "label": "Z" - }, - "0x001e": { - "group": "basic", - "key": "KC_1", - "label": "1" - }, - "0x001f": { - "group": "basic", - "key": "KC_2", - "label": "2" - }, - "0x0020": { - "group": "basic", - "key": "KC_3", - "label": "3" - }, - "0x0021": { - "group": "basic", - "key": "KC_4", - "label": "4" - }, - "0x0022": { - "group": "basic", - "key": "KC_5", - "label": "5" - }, - "0x0023": { - "group": "basic", - "key": "KC_6", - "label": "6" - }, - "0x0024": { - "group": "basic", - "key": "KC_7", - "label": "7" - }, - "0x0025": { - "group": "basic", - "key": "KC_8", - "label": "8" - }, - "0x0026": { - "group": "basic", - "key": "KC_9", - "label": "9" - }, - "0x0027": { - "group": "basic", - "key": "KC_0", - "label": "0" - }, - "0x0028": { - "group": "basic", - "key": "KC_ENTER", - "label": "Enter", - "aliases": [ - "KC_ENT" - ] - }, - "0x0029": { - "group": "basic", - "key": "KC_ESCAPE", - "label": "Esc", - "aliases": [ - "KC_ESC" - ] - }, - "0x002a": { - "group": "basic", - "key": "KC_BACKSPACE", - "label": "Backspace", - "aliases": [ - "KC_BSPC" - ] - }, - "0x002b": { - "group": "basic", - "key": "KC_TAB", - "label": "Tab" - }, - "0x002c": { - "group": "basic", - "key": "KC_SPACE", - "label": "Spacebar", - "aliases": [ - "KC_SPC" - ] - }, - "0x002d": { - "group": "basic", - "key": "KC_MINUS", - "label": "-", - "aliases": [ - "KC_MINS" - ] - }, - "0x002e": { - "group": "basic", - "key": "KC_EQUAL", - "label": "=", - "aliases": [ - "KC_EQL" - ] - }, - "0x002f": { - "group": "basic", - "key": "KC_LEFT_BRACKET", - "label": "]", - "aliases": [ - "KC_LBRC" - ] - }, - "0x0030": { - "group": "basic", - "key": "KC_RIGHT_BRACKET", - "label": "[", - "aliases": [ - "KC_RBRC" - ] - }, - "0x0031": { - "group": "basic", - "key": "KC_BACKSLASH", - "label": "\\", - "aliases": [ - "KC_BSLS" - ] - }, - "0x0032": { - "group": "basic", - "key": "KC_NONUS_HASH", - "label": "#", - "aliases": [ - "KC_NUHS" - ] - }, - "0x0033": { - "group": "basic", - "key": "KC_SEMICOLON", - "label": ";", - "aliases": [ - "KC_SCLN" - ] - }, - "0x0034": { - "group": "basic", - "key": "KC_QUOTE", - "label": "'", - "aliases": [ - "KC_QUOT" - ] - }, - "0x0035": { - "group": "basic", - "key": "KC_GRAVE", - "label": "`", - "aliases": [ - "KC_GRV" - ] - }, - "0x0036": { - "group": "basic", - "key": "KC_COMMA", - "label": ",", - "aliases": [ - "KC_COMM" - ] - }, - "0x0037": { - "group": "basic", - "key": "KC_DOT", - "label": "." - }, - "0x0038": { - "group": "basic", - "key": "KC_SLASH", - "label": "/", - "aliases": [ - "KC_SLSH" - ] - }, - "0x0039": { - "group": "basic", - "key": "KC_CAPS_LOCK", - "label": "Caps Lock", - "aliases": [ - "KC_CAPS" - ] - }, - "0x003a": { - "group": "basic", - "key": "KC_F1", - "label": "F1" - }, - "0x003b": { - "group": "basic", - "key": "KC_F2", - "label": "F2" - }, - "0x003c": { - "group": "basic", - "key": "KC_F3", - "label": "F3" - }, - "0x003d": { - "group": "basic", - "key": "KC_F4", - "label": "F4" - }, - "0x003e": { - "group": "basic", - "key": "KC_F5", - "label": "F5" - }, - "0x003f": { - "group": "basic", - "key": "KC_F6", - "label": "F6" - }, - "0x0040": { - "group": "basic", - "key": "KC_F7", - "label": "F7" - }, - "0x0041": { - "group": "basic", - "key": "KC_F8", - "label": "F8" - }, - "0x0042": { - "group": "basic", - "key": "KC_F9", - "label": "F9" - }, - "0x0043": { - "group": "basic", - "key": "KC_F10", - "label": "F10" - }, - "0x0044": { - "group": "basic", - "key": "KC_F11", - "label": "F11" - }, - "0x0045": { - "group": "basic", - "key": "KC_F12", - "label": "F12" - }, - "0x0046": { - "group": "basic", - "key": "KC_PRINT_SCREEN", - "label": "Print Screen", - "aliases": [ - "KC_PSCR" - ] - }, - "0x0047": { - "group": "basic", - "key": "KC_SCROLL_LOCK", - "label": "Scroll Lock", - "aliases": [ - "KC_SCRL", - "KC_BRMD" - ] - }, - "0x0048": { - "group": "basic", - "key": "KC_PAUSE", - "label": "Pause", - "aliases": [ - "KC_PAUS", - "KC_BRK", - "KC_BRMU" - ] - }, - "0x0049": { - "group": "basic", - "key": "KC_INSERT", - "label": "Insert", - "aliases": [ - "KC_INS" - ] - }, - "0x004a": { - "group": "basic", - "key": "KC_HOME", - "label": "Home" - }, - "0x004b": { - "group": "basic", - "key": "KC_PAGE_UP", - "label": "Page Up", - "aliases": [ - "KC_PGUP" - ] - }, - "0x004c": { - "group": "basic", - "key": "KC_DELETE", - "label": "Delete", - "aliases": [ - "KC_DEL" - ] - }, - "0x004d": { - "group": "basic", - "key": "KC_END", - "label": "End" - }, - "0x004e": { - "group": "basic", - "key": "KC_PAGE_DOWN", - "label": "Page Down", - "aliases": [ - "KC_PGDN" - ] - }, - "0x004f": { - "group": "basic", - "key": "KC_RIGHT", - "label": "Right", - "aliases": [ - "KC_RGHT" - ] - }, - "0x0050": { - "group": "basic", - "key": "KC_LEFT", - "label": "Left" - }, - "0x0051": { - "group": "basic", - "key": "KC_DOWN", - "label": "Down" - }, - "0x0052": { - "group": "basic", - "key": "KC_UP", - "label": "Up" - }, - "0x0053": { - "group": "basic", - "key": "KC_NUM_LOCK", - "label": "Num Lock", - "aliases": [ - "KC_NUM" - ] - }, - "0x0054": { - "group": "basic", - "key": "KC_KP_SLASH", - "label": "/", - "aliases": [ - "KC_PSLS" - ] - }, - "0x0055": { - "group": "basic", - "key": "KC_KP_ASTERISK", - "label": "*", - "aliases": [ - "KC_PAST" - ] - }, - "0x0056": { - "group": "basic", - "key": "KC_KP_MINUS", - "label": "-", - "aliases": [ - "KC_PMNS" - ] - }, - "0x0057": { - "group": "basic", - "key": "KC_KP_PLUS", - "label": "+", - "aliases": [ - "KC_PPLS" - ] - }, - "0x0058": { - "group": "basic", - "key": "KC_KP_ENTER", - "label": "Enter", - "aliases": [ - "KC_PENT" - ] - }, - "0x0059": { - "group": "basic", - "key": "KC_KP_1", - "label": "1", - "aliases": [ - "KC_P1" - ] - }, - "0x005a": { - "group": "basic", - "key": "KC_KP_2", - "label": "2", - "aliases": [ - "KC_P2" - ] - }, - "0x005b": { - "group": "basic", - "key": "KC_KP_3", - "label": "3", - "aliases": [ - "KC_P3" - ] - }, - "0x005c": { - "group": "basic", - "key": "KC_KP_4", - "label": "4", - "aliases": [ - "KC_P4" - ] - }, - "0x005d": { - "group": "basic", - "key": "KC_KP_5", - "label": "5", - "aliases": [ - "KC_P5" - ] - }, - "0x005e": { - "group": "basic", - "key": "KC_KP_6", - "label": "6", - "aliases": [ - "KC_P6" - ] - }, - "0x005f": { - "group": "basic", - "key": "KC_KP_7", - "label": "7", - "aliases": [ - "KC_P7" - ] - }, - "0x0060": { - "group": "basic", - "key": "KC_KP_8", - "label": "8", - "aliases": [ - "KC_P8" - ] - }, - "0x0061": { - "group": "basic", - "key": "KC_KP_9", - "label": "9", - "aliases": [ - "KC_P9" - ] - }, - "0x0062": { - "group": "basic", - "key": "KC_KP_0", - "label": "0", - "aliases": [ - "KC_P0" - ] - }, - "0x0063": { - "group": "basic", - "key": "KC_KP_DOT", - "label": ".", - "aliases": [ - "KC_PDOT" - ] - }, - "0x0064": { - "group": "basic", - "key": "KC_NONUS_BACKSLASH", - "label": "\\", - "aliases": [ - "KC_NUBS" - ] - }, - "0x0065": { - "group": "basic", - "key": "KC_APPLICATION", - "label": "Application", - "aliases": [ - "KC_APP" - ] - }, - "0x0066": { - "group": "basic", - "key": "KC_KB_POWER", - "label": "Application" - }, - "0x0067": { - "group": "basic", - "key": "KC_KP_EQUAL", - "label": "=", - "aliases": [ - "KC_PEQL" - ] - }, - "0x0068": { - "group": "basic", - "key": "KC_F13", - "label": "F13" - }, - "0x0069": { - "group": "basic", - "key": "KC_F14", - "label": "F14" - }, - "0x006a": { - "group": "basic", - "key": "KC_F15", - "label": "F15" - }, - "0x006b": { - "group": "basic", - "key": "KC_F16", - "label": "F16" - }, - "0x006c": { - "group": "basic", - "key": "KC_F17", - "label": "F17" - }, - "0x006d": { - "group": "basic", - "key": "KC_F18", - "label": "F18" - }, - "0x006e": { - "group": "basic", - "key": "KC_F19", - "label": "F19" - }, - "0x006f": { - "group": "basic", - "key": "KC_F20", - "label": "F20" - }, - "0x0070": { - "group": "basic", - "key": "KC_F21", - "label": "F21" - }, - "0x0071": { - "group": "basic", - "key": "KC_F22", - "label": "F22" - }, - "0x0072": { - "group": "basic", - "key": "KC_F23", - "label": "F23" - }, - "0x0073": { - "group": "basic", - "key": "KC_F24", - "label": "F24" - }, - "0x0074": { - "group": "basic", - "key": "KC_EXECUTE", - "label": "Execute", - "aliases": [ - "KC_EXEC" - ] - }, - "0x0075": { - "group": "basic", - "key": "KC_HELP", - "label": "Help" - }, - "0x0076": { - "group": "basic", - "key": "KC_MENU", - "label": "Menu" - }, - "0x0077": { - "group": "basic", - "key": "KC_SELECT", - "label": "Select", - "aliases": [ - "KC_SLCT" - ] - }, - "0x0078": { - "group": "basic", - "key": "KC_STOP", - "label": "Stop" - }, - "0x0079": { - "group": "basic", - "key": "KC_AGAIN", - "label": "Again", - "aliases": [ - "KC_AGIN" - ] - }, - "0x007a": { - "group": "basic", - "key": "KC_UNDO", - "label": "Undo" - }, - "0x007b": { - "group": "basic", - "key": "KC_CUT", - "label": "Cut" - }, - "0x007c": { - "group": "basic", - "key": "KC_COPY", - "label": "Copy" - }, - "0x007d": { - "group": "basic", - "key": "KC_PASTE", - "label": "Paste", - "aliases": [ - "KC_PSTE" - ] - }, - "0x007e": { - "group": "basic", - "key": "KC_FIND", - "label": "Find" - }, - "0x007f": { - "group": "basic", - "key": "KC_KB_MUTE", - "label": "Mute" - }, - "0x0080": { - "group": "basic", - "key": "KC_KB_VOLUME_UP", - "label": "Volume Up" - }, - "0x0081": { - "group": "basic", - "key": "KC_KB_VOLUME_DOWN", - "label": "Volume Down" - }, - "0x0082": { - "group": "basic", - "key": "KC_LOCKING_CAPS_LOCK", - "label": "Caps Lock", - "aliases": [ - "KC_LCAP" - ] - }, - "0x0083": { - "group": "basic", - "key": "KC_LOCKING_NUM_LOCK", - "label": "Num Lock", - "aliases": [ - "KC_LNUM" - ] - }, - "0x0084": { - "group": "basic", - "key": "KC_LOCKING_SCROLL_LOCK", - "label": "Scroll Lock", - "aliases": [ - "KC_LSCR" - ] - }, - "0x0085": { - "group": "basic", - "key": "KC_KP_COMMA", - "label": ",", - "aliases": [ - "KC_PCMM" - ] - }, - "0x0086": { - "group": "basic", - "key": "KC_KP_EQUAL_AS400", - "label": "=" - }, - "0x0087": { - "group": "basic", - "key": "KC_INTERNATIONAL_1", - "label": "INT 1", - "aliases": [ - "KC_INT1" - ] - }, - "0x0088": { - "group": "basic", - "key": "KC_INTERNATIONAL_2", - "label": "INT 2", - "aliases": [ - "KC_INT2" - ] - }, - "0x0089": { - "group": "basic", - "key": "KC_INTERNATIONAL_3", - "label": "INT 3", - "aliases": [ - "KC_INT3" - ] - }, - "0x008a": { - "group": "basic", - "key": "KC_INTERNATIONAL_4", - "label": "INT 4", - "aliases": [ - "KC_INT4" - ] - }, - "0x008b": { - "group": "basic", - "key": "KC_INTERNATIONAL_5", - "label": "INT 5", - "aliases": [ - "KC_INT5" - ] - }, - "0x008c": { - "group": "basic", - "key": "KC_INTERNATIONAL_6", - "label": "INT 6", - "aliases": [ - "KC_INT6" - ] - }, - "0x008d": { - "group": "basic", - "key": "KC_INTERNATIONAL_7", - "label": "INT 7", - "aliases": [ - "KC_INT7" - ] - }, - "0x008e": { - "group": "basic", - "key": "KC_INTERNATIONAL_8", - "label": "INT 8", - "aliases": [ - "KC_INT8" - ] - }, - "0x008f": { - "group": "basic", - "key": "KC_INTERNATIONAL_9", - "label": "INT 9", - "aliases": [ - "KC_INT9" - ] - }, - "0x0090": { - "group": "basic", - "key": "KC_LANGUAGE_1", - "label": "LANG 1", - "aliases": [ - "KC_LNG1" - ] - }, - "0x0091": { - "group": "basic", - "key": "KC_LANGUAGE_2", - "label": "LANG 2", - "aliases": [ - "KC_LNG2" - ] - }, - "0x0092": { - "group": "basic", - "key": "KC_LANGUAGE_3", - "label": "LANG 3", - "aliases": [ - "KC_LNG3" - ] - }, - "0x0093": { - "group": "basic", - "key": "KC_LANGUAGE_4", - "label": "LANG 4", - "aliases": [ - "KC_LNG4" - ] - }, - "0x0094": { - "group": "basic", - "key": "KC_LANGUAGE_5", - "label": "LANG 5", - "aliases": [ - "KC_LNG5" - ] - }, - "0x0095": { - "group": "basic", - "key": "KC_LANGUAGE_6", - "label": "LANG 6", - "aliases": [ - "KC_LNG6" - ] - }, - "0x0096": { - "group": "basic", - "key": "KC_LANGUAGE_7", - "label": "LANG 7", - "aliases": [ - "KC_LNG7" - ] - }, - "0x0097": { - "group": "basic", - "key": "KC_LANGUAGE_8", - "label": "LANG 8", - "aliases": [ - "KC_LNG8" - ] - }, - "0x0098": { - "group": "basic", - "key": "KC_LANGUAGE_9", - "label": "LANG 9", - "aliases": [ - "KC_LNG9" - ] - }, - "0x0099": { - "group": "basic", - "key": "KC_ALTERNATE_ERASE", - "label": "Alternate Erase", - "aliases": [ - "KC_ERAS" - ] - }, - "0x009a": { - "group": "basic", - "key": "KC_SYSTEM_REQUEST", - "label": "SysReq/Attention", - "aliases": [ - "KC_SYRQ" - ] - }, - "0x009b": { - "group": "basic", - "key": "KC_CANCEL", - "label": "Cancel", - "aliases": [ - "KC_CNCL" - ] - }, - "0x009c": { - "group": "basic", - "key": "KC_CLEAR", - "label": "Clear", - "aliases": [ - "KC_CLR" - ] - }, - "0x009d": { - "group": "basic", - "key": "KC_PRIOR", - "label": "Prior", - "aliases": [ - "KC_PRIR" - ] - }, - "0x009e": { - "group": "basic", - "key": "KC_RETURN", - "label": "Return", - "aliases": [ - "KC_RETN" - ] - }, - "0x009f": { - "group": "basic", - "key": "KC_SEPARATOR", - "label": "Separator", - "aliases": [ - "KC_SEPR" - ] - }, - "0x00a0": { - "group": "basic", - "key": "KC_OUT", - "label": "Out" - }, - "0x00a1": { - "group": "basic", - "key": "KC_OPER", - "label": "Oper" - }, - "0x00a2": { - "group": "basic", - "key": "KC_CLEAR_AGAIN", - "label": "Clear/Again", - "aliases": [ - "KC_CLAG" - ] - }, - "0x00a3": { - "group": "basic", - "key": "KC_CRSEL", - "label": "CrSel/Props", - "aliases": [ - "KC_CRSL" - ] - }, - "0x00a4": { - "group": "basic", - "key": "KC_EXSEL", - "label": "ExSel", - "aliases": [ - "KC_EXSL" - ] - }, - - "0x00a5": { - "group": "media", - "key": "KC_SYSTEM_POWER", - "label": "System Power Down", - "aliases": [ - "KC_PWR" - ] - }, - "0x00a6": { - "group": "media", - "key": "KC_SYSTEM_SLEEP", - "label": "System Sleep", - "aliases": [ - "KC_SLEP" - ] - }, - "0x00a7": { - "group": "media", - "key": "KC_SYSTEM_WAKE", - "label": "System Wake", - "aliases": [ - "KC_WAKE" - ] - }, - "0x00a8": { - "group": "media", - "key": "KC_AUDIO_MUTE", - "label": "Mute", - "aliases": [ - "KC_MUTE" - ] - }, - "0x00a9": { - "group": "media", - "key": "KC_AUDIO_VOL_UP", - "label": "Volume Up", - "aliases": [ - "KC_VOLU" - ] - }, - "0x00aa": { - "group": "media", - "key": "KC_AUDIO_VOL_DOWN", - "label": "Volume Down", - "aliases": [ - "KC_VOLD" - ] - }, - "0x00ab": { - "group": "media", - "key": "KC_MEDIA_NEXT_TRACK", - "label": "Next", - "aliases": [ - "KC_MNXT" - ] - }, - "0x00ac": { - "group": "media", - "key": "KC_MEDIA_PREV_TRACK", - "label": "Previous", - "aliases": [ - "KC_MPRV" - ] - }, - "0x00ad": { - "group": "media", - "key": "KC_MEDIA_STOP", - "label": "Stop", - "aliases": [ - "KC_MSTP" - ] - }, - "0x00ae": { - "group": "media", - "key": "KC_MEDIA_PLAY_PAUSE", - "label": "Mute", - "aliases": [ - "KC_MPLY" - ] - }, - "0x00af": { - "group": "media", - "key": "KC_MEDIA_SELECT", - "label": "Launch Player", - "aliases": [ - "KC_MSEL" - ] - }, - "0x00b0": { - "group": "media", - "key": "KC_MEDIA_EJECT", - "label": "Eject", - "aliases": [ - "KC_EJCT" - ] - }, - "0x00b1": { - "group": "media", - "key": "KC_MAIL", - "label": "Launch Mail" - }, - "0x00b2": { - "group": "media", - "key": "KC_CALCULATOR", - "label": "Launch Calculator", - "aliases": [ - "KC_CALC" - ] - }, - "0x00b3": { - "group": "media", - "key": "KC_MY_COMPUTER", - "label": "Launch My Computer", - "aliases": [ - "KC_MYCM" - ] - }, - "0x00b4": { - "group": "media", - "key": "KC_WWW_SEARCH", - "label": "Browser Search", - "aliases": [ - "KC_WSCH" - ] - }, - "0x00b5": { - "group": "media", - "key": "KC_WWW_HOME", - "label": "Browser Home", - "aliases": [ - "KC_WHOM" - ] - }, - "0x00b6": { - "group": "media", - "key": "KC_WWW_BACK", - "label": "Browser Back", - "aliases": [ - "KC_WBAK" - ] - }, - "0x00b7": { - "group": "media", - "key": "KC_WWW_FORWARD", - "label": "Browser Forward", - "aliases": [ - "KC_WFWD" - ] - }, - "0x00b8": { - "group": "media", - "key": "KC_WWW_STOP", - "label": "Browser Stop", - "aliases": [ - "KC_WSTP" - ] - }, - "0x00b9": { - "group": "media", - "key": "KC_WWW_REFRESH", - "label": "Browser Refresh", - "aliases": [ - "KC_WREF" - ] - }, - "0x00ba": { - "group": "media", - "key": "KC_WWW_FAVORITES", - "label": "Browser Favorites", - "aliases": [ - "KC_WFAV" - ] - }, - "0x00bb": { - "group": "media", - "key": "KC_MEDIA_FAST_FORWARD", - "label": "Next Track", - "aliases": [ - "KC_MFFD" - ] - }, - "0x00bc": { - "group": "media", - "key": "KC_MEDIA_REWIND", - "label": "Previous Track", - "aliases": [ - "KC_MRWD" - ] - }, - "0x00bd": { - "group": "media", - "key": "KC_BRIGHTNESS_UP", - "label": "Brightness Up", - "aliases": [ - "KC_BRIU" - ] - }, - "0x00be": { - "group": "media", - "key": "KC_BRIGHTNESS_DOWN", - "label": "Brightness Down", - "aliases": [ - "KC_BRID" - ] - }, - - "0x00e0": { - "group": "modifiers", - "key": "KC_LEFT_CTRL", - "label": "Left Control", - "aliases": [ - "KC_LCTL" - ] - }, - "0x00e1": { - "group": "modifiers", - "key": "KC_LEFT_SHIFT", - "label": "Left Shift", - "aliases": [ - "KC_LSFT" - ] - }, - "0x00e2": { - "group": "modifiers", - "key": "KC_LEFT_ALT", - "label": "Left Alt", - "aliases": [ - "KC_LALT", - "KC_LOPT" - ] - }, - "0x00e3": { - "group": "modifiers", - "key": "KC_LEFT_GUI", - "label": "Left GUI", - "aliases": [ - "KC_LGUI", - "KC_LCMD", - "KC_LWIN" - ] - }, - "0x00e4": { - "group": "modifiers", - "key": "KC_RIGHT_CTRL", - "label": "Right Control", - "aliases": [ - "KC_RCTL" - ] - }, - "0x00e5": { - "group": "modifiers", - "key": "KC_RIGHT_SHIFT", - "label": "Right Shift", - "aliases": [ - "KC_RSFT" - ] - }, - "0x00e6": { - "group": "modifiers", - "key": "KC_RIGHT_ALT", - "label": "Right Alt", - "aliases": [ - "KC_RALT", - "KC_ROPT", - "KC_ALGR" - ] - }, - "0x00e7": { - "group": "modifiers", - "key": "KC_RIGHT_GUI", - "label": "Right GUI", - "aliases": [ - "KC_RGUI", - "KC_RCMD", - "KC_RWIN" - ] - }, - - "0x00ed": { - "group": "mouse", - "key": "KC_MS_UP", - "label": "Move cursor up", - "aliases": [ - "KC_MS_U" - ] - }, - "0x00ee": { - "group": "mouse", - "key": "KC_MS_DOWN", - "label": "Move cursor down", - "aliases": [ - "KC_MS_D" - ] - }, - "0x00ef": { - "group": "mouse", - "key": "KC_MS_LEFT", - "label": "Move cursor left", - "aliases": [ - "KC_MS_L" - ] - }, - "0x00f0": { - "group": "mouse", - "key": "KC_MS_RIGHT", - "label": "Move cursor right", - "aliases": [ - "KC_MS_R" - ] - }, - "0x00f1": { - "group": "mouse", - "key": "KC_MS_BTN1", - "label": "Press button 1", - "aliases": [ - "KC_BTN1" - ] - }, - "0x00f2": { - "group": "mouse", - "key": "KC_MS_BTN2", - "label": "Press button 2", - "aliases": [ - "KC_BTN2" - ] - }, - "0x00f3": { - "group": "mouse", - "key": "KC_MS_BTN3", - "label": "Press button 3", - "aliases": [ - "KC_BTN3" - ] - }, - "0x00f4": { - "group": "mouse", - "key": "KC_MS_BTN4", - "label": "Press button 4", - "aliases": [ - "KC_BTN4" - ] - }, - "0x00f5": { - "group": "mouse", - "key": "KC_MS_BTN5", - "label": "Press button 5", - "aliases": [ - "KC_BTN5" - ] - }, - "0x00f6": { - "group": "mouse", - "key": "KC_MS_BTN6", - "label": "Press button 6", - "aliases": [ - "KC_BTN6" - ] - }, - "0x00f7": { - "group": "mouse", - "key": "KC_MS_BTN7", - "label": "Press button 7", - "aliases": [ - "KC_BTN7" - ] - }, - "0x00f8": { - "group": "mouse", - "key": "KC_MS_BTN8", - "label": "Press button 8", - "aliases": [ - "KC_BTN8" - ] - }, - "0x00f9": { - "group": "mouse", - "key": "KC_MS_WH_UP", - "label": "Move wheel up", - "aliases": [ - "KC_WH_U" - ] - }, - "0x00fa": { - "group": "mouse", - "key": "KC_MS_WH_DOWN", - "label": "Move wheel down", - "aliases": [ - "KC_WH_D" - ] - }, - "0x00fb": { - "group": "mouse", - "key": "KC_MS_WH_LEFT", - "label": "Move wheel left", - "aliases": [ - "KC_WH_L" - ] - }, - "0x00fc": { - "group": "mouse", - "key": "KC_MS_WH_RIGHT", - "label": "Move wheel right", - "aliases": [ - "KC_WH_R" - ] - }, - "0x00fd": { - "group": "mouse", - "key": "KC_MS_ACCEL0", - "label": "Set speed to 0", - "aliases": [ - "KC_ACL0" - ] - }, - "0x00fe": { - "group": "mouse", - "key": "KC_MS_ACCEL1", - "label": "Set speed to 1", - "aliases": [ - "KC_ACL1" - ] - }, - "0x00ff": { - "group": "mouse", - "key": "KC_MS_ACCEL2", - "label": "Set speed to 2", - "aliases": [ - "KC_ACL2" - ] - } - } -} \ No newline at end of file diff --git a/lib/python/qmk/cli/xap/xap.py b/lib/python/qmk/cli/xap/xap.py index e6643be65c8d..bf8879540703 100644 --- a/lib/python/qmk/cli/xap/xap.py +++ b/lib/python/qmk/cli/xap/xap.py @@ -44,7 +44,27 @@ def _load_keycodes(keycode_version): spec = load_spec(keycode_version) # 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()} + ret = {int(k, 16): v.get('aliases', [v.get('key')])[0] for k, v in spec['keycodes'].items()} + + # TODO: handle non static keycodes + for k, v in spec['ranges'].items(): + lo, mask = map(lambda x: int(x, 16), k.split('/')) + hi = lo + mask + define = v.get("define") + for i in range(lo, hi): + if i not in ret: + if define == 'QK_TO': + layer = i & 0x1F + ret[i] = f'TO({layer})' + elif define == 'QK_MOMENTARY': + layer = i & 0x1F + ret[i] = f'MO({layer})' + elif define == 'QK_LAYER_TAP': + layer = (((i) >> 8) & 0xF) + keycode = ((i) & 0xFF) + ret[i] = f'LT({layer}, {ret.get(keycode, "???")})' + + return ret def _list_devices(): From 7de1a9d48573cb7e6dcd7cac1bdaa58ac92b474a Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 17 Nov 2022 23:31:09 +0000 Subject: [PATCH 159/203] Fix insecure build --- quantum/xap/xap_handlers.c | 1 + 1 file changed, 1 insertion(+) diff --git a/quantum/xap/xap_handlers.c b/quantum/xap/xap_handlers.c index 9277be00b75e..73c83e5c28e5 100644 --- a/quantum/xap/xap_handlers.c +++ b/quantum/xap/xap_handlers.c @@ -24,6 +24,7 @@ # define secure_get_status() SECURE_UNLOCKED # define secure_request_unlock() # define secure_lock() +# define secure_is_unlocked() false #endif #ifdef DYNAMIC_KEYMAP_ENABLE From 0266b5be2d9da726fcd2c70c280c4a3761e81119 Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 17 Nov 2022 23:31:22 +0000 Subject: [PATCH 160/203] Expose xap_respond_success --- quantum/xap/xap.h | 1 + 1 file changed, 1 insertion(+) diff --git a/quantum/xap/xap.h b/quantum/xap/xap.h index 76e509549296..8c3252604417 100644 --- a/quantum/xap/xap.h +++ b/quantum/xap/xap.h @@ -29,6 +29,7 @@ # define XAP_SUBSYSTEM_VERSION_USER 0 #endif +void xap_respond_success(xap_token_t token); 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); From 763e3dfb136b65524396e3bf7c0fc62b55c069e6 Mon Sep 17 00:00:00 2001 From: zvecr Date: Fri, 18 Nov 2022 00:16:50 +0000 Subject: [PATCH 161/203] revert whitespace --- builddefs/build_keyboard.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builddefs/build_keyboard.mk b/builddefs/build_keyboard.mk index f629cb8fdcfb..499e6ffc69f5 100644 --- a/builddefs/build_keyboard.mk +++ b/builddefs/build_keyboard.mk @@ -1,4 +1,4 @@ - # Determine what keyboard we are building and setup the build environment. +# Determine what keyboard we are building and setup the build environment. # # We support folders up to 5 levels deep below `keyboards/`. This file is # responsible for determining which folder is being used and doing the From 2ba7232131b9fb9776f160f1b189e5caf669a711 Mon Sep 17 00:00:00 2001 From: zvecr Date: Fri, 18 Nov 2022 00:18:11 +0000 Subject: [PATCH 162/203] Remove duplicate comment --- lib/python/qmk/cli/generate/api.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/python/qmk/cli/generate/api.py b/lib/python/qmk/cli/generate/api.py index 9f43775bdbe3..2b89f769097b 100755 --- a/lib/python/qmk/cli/generate/api.py +++ b/lib/python/qmk/cli/generate/api.py @@ -143,8 +143,6 @@ def generate_api(cli): # Feature specific handling _resolve_keycode_specs(v1_dir) - - # Feature specific handling _resolve_xap_specs(v1_dir / 'xap') # Write the global JSON files From 646ced4441f40a3f9a38eb2a198c13229de17136 Mon Sep 17 00:00:00 2001 From: zvecr Date: Fri, 18 Nov 2022 00:23:20 +0000 Subject: [PATCH 163/203] Remove bad EECONFIG address --- quantum/eeconfig.h | 1 - 1 file changed, 1 deletion(-) diff --git a/quantum/eeconfig.h b/quantum/eeconfig.h index 6326bd775ef9..5615fb0ae2f8 100644 --- a/quantum/eeconfig.h +++ b/quantum/eeconfig.h @@ -30,7 +30,6 @@ along with this program. If not, see . #define EECONFIG_DEBUG (uint8_t *)2 #define EECONFIG_DEFAULT_LAYER (uint8_t *)3 #define EECONFIG_KEYMAP (uint16_t *)4 -#define EECONFIG_MOUSEKEY_ACCEL (uint8_t *)5 #define EECONFIG_BACKLIGHT (uint8_t *)6 #define EECONFIG_AUDIO (uint8_t *)7 #define EECONFIG_RGBLIGHT (uint32_t *)8 From b831eb52705ddfb1ccf650a8b2f00b0945a69a9d Mon Sep 17 00:00:00 2001 From: zvecr Date: Sat, 19 Nov 2022 00:33:02 +0000 Subject: [PATCH 164/203] Minor tidy up of xap_respond_success calls --- quantum/xap/xap.h | 2 +- quantum/xap/xap_handlers.c | 36 ++++++++++++------------------------ 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/quantum/xap/xap.h b/quantum/xap/xap.h index 8c3252604417..122b7055be6a 100644 --- a/quantum/xap/xap.h +++ b/quantum/xap/xap.h @@ -29,7 +29,7 @@ # define XAP_SUBSYSTEM_VERSION_USER 0 #endif -void xap_respond_success(xap_token_t token); +bool xap_respond_success(xap_token_t token); 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); diff --git a/quantum/xap/xap_handlers.c b/quantum/xap/xap_handlers.c index 73c83e5c28e5..625261af71a3 100644 --- a/quantum/xap/xap_handlers.c +++ b/quantum/xap/xap_handlers.c @@ -24,7 +24,6 @@ # define secure_get_status() SECURE_UNLOCKED # define secure_request_unlock() # define secure_lock() -# define secure_is_unlocked() false #endif #ifdef DYNAMIC_KEYMAP_ENABLE @@ -33,8 +32,9 @@ # define keymap_max_layer_count() keymap_layer_count() #endif -void xap_respond_success(xap_token_t token) { +bool xap_respond_success(xap_token_t token) { xap_send(token, XAP_RESPONSE_FLAG_SUCCESS, NULL, 0); + return true; } void xap_respond_failure(xap_token_t token, xap_response_flags_t response_flags) { @@ -57,10 +57,6 @@ bool xap_respond_u32(xap_token_t token, uint32_t 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; @@ -86,12 +82,12 @@ bool xap_respond_secure_status(xap_token_t token, const void *data, size_t lengt 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); + return xap_respond_success(token); } bool xap_respond_secure_lock(xap_token_t token, const void *data, size_t length) { secure_lock(); - return xap_respond_data(token, NULL, 0); + return xap_respond_success(token); } #ifdef BOOTLOADER_JUMP_SUPPORTED @@ -172,8 +168,7 @@ bool xap_respond_dynamic_keymap_set_keycode(xap_token_t token, const void *data, } dynamic_keymap_set_keycode(arg->layer, arg->row, arg->column, arg->keycode); - xap_respond_success(token); - return true; + return xap_respond_success(token); } #endif @@ -190,8 +185,7 @@ bool xap_respond_dynamic_encoder_set_keycode(xap_token_t token, const void *data } dynamic_keymap_set_encoder(arg->layer, arg->encoder, arg->clockwise, arg->keycode); - xap_respond_success(token); - return true; + return xap_respond_success(token); } #endif @@ -233,15 +227,13 @@ bool xap_respond_set_backlight_config(xap_token_t token, const void *data, size_ } # endif - xap_respond_success(token); - return true; + return xap_respond_success(token); } bool xap_respond_save_backlight_config(xap_token_t token, const void *data, size_t length) { eeconfig_update_backlight_current(); - xap_respond_success(token); - return true; + return xap_respond_success(token); } #endif @@ -287,15 +279,13 @@ bool xap_respond_set_rgblight_config(xap_token_t token, const void *data, size_t rgblight_sethsv_noeeprom(arg->hue, arg->sat, arg->val); rgblight_set_speed_noeeprom(arg->speed); - xap_respond_success(token); - return true; + return xap_respond_success(token); } bool xap_respond_save_rgblight_config(xap_token_t token, const void *data, size_t length) { eeconfig_update_rgblight_current(); - xap_respond_success(token); - return true; + return xap_respond_success(token); } #endif @@ -343,14 +333,12 @@ bool xap_respond_set_rgb_matrix_config(xap_token_t token, const void *data, size rgb_matrix_set_speed_noeeprom(arg->speed); rgb_matrix_set_flags_noeeprom(arg->flags); - xap_respond_success(token); - return true; + return xap_respond_success(token); } bool xap_respond_save_rgb_matrix_config(xap_token_t token, const void *data, size_t length) { eeconfig_update_rgb_matrix(); - xap_respond_success(token); - return true; + return xap_respond_success(token); } #endif From ff2f0d943b88037160327bef6da4006089d887ae Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 23 Nov 2022 17:43:55 +0000 Subject: [PATCH 165/203] ChibiOS - Non blocking xap_send_base --- keyboards/zvecr/zv48/keymaps/xap/keymap.c | 11 +++++++++++ tmk_core/protocol/chibios/usb_main.c | 8 +++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/keyboards/zvecr/zv48/keymaps/xap/keymap.c b/keyboards/zvecr/zv48/keymaps/xap/keymap.c index c45e573bd93f..36828f54e465 100644 --- a/keyboards/zvecr/zv48/keymaps/xap/keymap.c +++ b/keyboards/zvecr/zv48/keymaps/xap/keymap.c @@ -100,3 +100,14 @@ const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][2] = { [_ADJUST] = { ENCODER_CCW_CW(RGB_RMOD, RGB_MOD), ENCODER_CCW_CW(KC_RIGHT, KC_LEFT) }, }; #endif + +void housekeeping_task_user(void) { + static uint32_t timer = 0; + static uint8_t count = 0; + if (timer_elapsed32(timer) > 1000) { + timer = timer_read32(); + count++; + + xap_broadcast(0x03, &count, 1); + } +} diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index ae8386c1b008..ef9dbf8edc96 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -1138,7 +1138,13 @@ void xap_send_base(uint8_t *data, uint8_t length) { if (length != XAP_EPSIZE) { return; } - chnWrite(&drivers.xap_driver.driver, data, length); + + // see console sendchar for timeout details + static bool timed_out = false; + + const sysinterval_t timeout = timed_out ? TIME_IMMEDIATE : TIME_MS2I(5); + const size_t result = chnWriteTimeout(&drivers.xap_driver.driver, data, length, timeout); + timed_out = (result == 0); } void xap_send(xap_token_t token, xap_response_flags_t response_flags, const void *data, size_t length) { From f99f8a22777dcf16507797b650202d65a3978589 Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 1 Dec 2022 19:07:10 +0000 Subject: [PATCH 166/203] Fix zvecr/zv48:xap --- keyboards/zvecr/zv48/keymaps/xap/keymap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keyboards/zvecr/zv48/keymaps/xap/keymap.c b/keyboards/zvecr/zv48/keymaps/xap/keymap.c index 36828f54e465..b9d3ceec6eee 100644 --- a/keyboards/zvecr/zv48/keymaps/xap/keymap.c +++ b/keyboards/zvecr/zv48/keymaps/xap/keymap.c @@ -80,7 +80,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { * `-----------------------------------------------------------------------------------' */ [_ADJUST] = LAYOUT_ortho_4x12( - _______, RESET, _______, _______, _______, _______, RGB_TOG, RGB_MOD, RGB_RMOD,RGB_M_G, RESET, _______, + _______, QK_BOOT, _______, _______, _______, _______, RGB_TOG, RGB_MOD, RGB_RMOD,RGB_M_G, QK_BOOT, _______, _______, _______, _______, _______, _______, _______, RGB_HUI, RGB_SAI, RGB_VAI, _______, _______, _______, _______, _______, _______, _______, _______, _______, RGB_HUD, RGB_SAD, RGB_VAD, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______ From 0431558da01d7065f16aa23f0e3cd6b196971114 Mon Sep 17 00:00:00 2001 From: zvecr Date: Fri, 2 Dec 2022 22:48:00 +0000 Subject: [PATCH 167/203] Mock up audio routes to match via --- data/xap/xap_0.3.0.hjson | 71 ++++++++ lib/python/xap_client/routes.py | 5 + lib/python/xap_client/types.py | 2 + quantum/xap/handlers/audio.c | 39 ++++ quantum/xap/handlers/core.c | 67 +++++++ quantum/xap/handlers/lighting.c | 159 ++++++++++++++++ quantum/xap/handlers/remapping.c | 82 +++++++++ quantum/xap/xap_handlers.c | 300 +------------------------------ 8 files changed, 431 insertions(+), 294 deletions(-) create mode 100644 quantum/xap/handlers/audio.c create mode 100644 quantum/xap/handlers/core.c create mode 100644 quantum/xap/handlers/lighting.c create mode 100644 quantum/xap/handlers/remapping.c diff --git a/data/xap/xap_0.3.0.hjson b/data/xap/xap_0.3.0.hjson index 1bd24c47f2d1..a119e6a42fc2 100644 --- a/data/xap/xap_0.3.0.hjson +++ b/data/xap/xap_0.3.0.hjson @@ -321,5 +321,76 @@ } } } + + 0x07: { + type: router + name: Audio + define: AUDIO + description: + ''' + This subsystem allows for control over the audio subsystem. + ''' + enable_if_preprocessor: defined(AUDIO_ENABLE) + routes: { + 0x01: { + type: command + name: Capabilities Query + define: CAPABILITIES_QUERY + description: + ''' + Audio 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_AUDIO_CAPABILITIES + } + + 0x03: { + type: command + name: Get Config + define: GET_CONFIG + description: Query the current config. + return_type: struct + return_struct_length: 2 + return_struct_members: [ + { + type: u8 + name: enable + }, + { + type: u8 + name: clicky_enable + }, + ] + return_execute: get_audio_config + } + 0x04: { + type: command + name: Set Config + define: SET_CONFIG + description: Set the current config. + request_type: struct + request_struct_length: 2 + request_struct_members: [ + { + type: u8 + name: enable + }, + { + type: u8 + name: clicky_enable + }, + ] + return_execute: set_audio_config + } + 0x05: { + type: command + name: Save Config + define: SAVE_CONFIG + description: Save the current config. + return_execute: save_audio_config + } + } + } } } diff --git a/lib/python/xap_client/routes.py b/lib/python/xap_client/routes.py index 658c486d0410..12f3c426f2d3 100644 --- a/lib/python/xap_client/routes.py +++ b/lib/python/xap_client/routes.py @@ -79,3 +79,8 @@ class XAPRoutes(): LIGHTING_RGB_MATRIX_GET_CONFIG = b'\x06\x04\x03' LIGHTING_RGB_MATRIX_SET_CONFIG = b'\x06\x04\x04' LIGHTING_RGB_MATRIX_SAVE_CONFIG = b'\x06\x04\x05' + # AUDIO + AUDIO_CAPABILITIES_QUERY = b'\x07\x01' + AUDIO_GET_CONFIG = b'\x07\x03' + AUDIO_SET_CONFIG = b'\x07\x04' + AUDIO_SAVE_CONFIG = b'\x07\x05' diff --git a/lib/python/xap_client/types.py b/lib/python/xap_client/types.py index 7ad25fc0d123..bfdf48961fef 100644 --- a/lib/python/xap_client/types.py +++ b/lib/python/xap_client/types.py @@ -113,6 +113,8 @@ def to_bytes(self): # TODO: gen inbound object for set_config # TODO: gen outbound object for get_config # TODO: gen inbound object for set_config +# TODO: gen outbound object for get_config +# TODO: gen inbound object for set_config class XAPSecureStatus(IntEnum): diff --git a/quantum/xap/handlers/audio.c b/quantum/xap/handlers/audio.c new file mode 100644 index 000000000000..b70a7f9c4f95 --- /dev/null +++ b/quantum/xap/handlers/audio.c @@ -0,0 +1,39 @@ +// Copyright 2022 QMK +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "quantum.h" +#include "xap.h" + +#if ((defined(AUDIO_ENABLE))) +# include "audio.h" + +extern audio_config_t audio_config; + +bool xap_respond_get_audio_config(xap_token_t token, const void *data, size_t length) { + xap_route_audio_get_config_t ret; + + ret.enable = audio_config.enable; + ret.clicky_enable = audio_config.clicky_enable; + + return xap_respond_data(token, &ret, sizeof(ret)); +} + +bool xap_respond_set_audio_config(xap_token_t token, const void *data, size_t length) { + if (length != sizeof(xap_route_audio_set_config_arg_t)) { + return false; + } + + xap_route_audio_set_config_arg_t *arg = (xap_route_audio_set_config_arg_t *)data; + + audio_config.enable = arg->enable; + audio_config.clicky_enable = arg->clicky_enable; + + return xap_respond_success(token); +} + +bool xap_respond_save_audio_config(xap_token_t token, const void *data, size_t length) { + eeconfig_update_audio(audio_config.raw); + + return xap_respond_success(token); +} +#endif diff --git a/quantum/xap/handlers/core.c b/quantum/xap/handlers/core.c new file mode 100644 index 000000000000..7969dc79759f --- /dev/null +++ b/quantum/xap/handlers/core.c @@ -0,0 +1,67 @@ +// Copyright 2022 QMK +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "quantum.h" +#include "xap.h" +#include "hardware_id.h" + +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; + memcpy(&offset, data, sizeof(uint16_t)); + + xap_route_qmk_config_blob_chunk_t ret = {0}; + + bool get_config_blob_chunk(uint16_t offset, uint8_t * data, uint8_t data_len); + if (!get_config_blob_chunk(offset, (uint8_t *)&ret, sizeof(ret))) { + return false; + } + + 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_success(token); +} + +bool xap_respond_secure_lock(xap_token_t token, const void *data, size_t length) { + secure_lock(); + return xap_respond_success(token); +} + +#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 + +#ifndef NO_RESET +bool xap_respond_request_eeprom_reset(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)); + eeconfig_disable(); + soft_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)); +} \ No newline at end of file diff --git a/quantum/xap/handlers/lighting.c b/quantum/xap/handlers/lighting.c new file mode 100644 index 000000000000..56726d19ac8a --- /dev/null +++ b/quantum/xap/handlers/lighting.c @@ -0,0 +1,159 @@ +// Copyright 2022 QMK +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "quantum.h" +#include "xap.h" + +#define INVALID_EFFECT 0xFF + +#if ((defined(BACKLIGHT_ENABLE))) +# include "backlight.h" + +extern backlight_config_t backlight_config; + +bool xap_respond_get_backlight_config(xap_token_t token, const void *data, size_t length) { + xap_route_lighting_backlight_get_config_t ret; + + ret.enable = backlight_config.enable; + ret.mode = backlight_config.breathing; + ret.val = backlight_config.level; + + return xap_respond_data(token, &ret, sizeof(ret)); +} + +bool xap_respond_set_backlight_config(xap_token_t token, const void *data, size_t length) { + if (length != sizeof(xap_route_lighting_backlight_set_config_arg_t)) { + return false; + } + + xap_route_lighting_backlight_set_config_arg_t *arg = (xap_route_lighting_backlight_set_config_arg_t *)data; + + if (arg->enable) { + backlight_level_noeeprom(arg->val); + } else { + backlight_level_noeeprom(0); + } + +# ifdef BACKLIGHT_BREATHING + if (arg->mode) { + backlight_enable_breathing(); + } else { + backlight_disable_breathing(); + } +# endif + + return xap_respond_success(token); +} + +bool xap_respond_save_backlight_config(xap_token_t token, const void *data, size_t length) { + eeconfig_update_backlight_current(); + + return xap_respond_success(token); +} +#endif + +#if ((defined(RGBLIGHT_ENABLE))) +# include "rgblight.h" + +extern rgblight_config_t rgblight_config; + +uint8_t rgblight2xap(uint8_t val); +uint8_t xap2rgblight(uint8_t val); + +void rgblight_enabled_noeeprom(bool val) { + val ? rgblight_enable_noeeprom() : rgblight_disable_noeeprom(); +} + +bool xap_respond_get_rgblight_config(xap_token_t token, const void *data, size_t length) { + xap_route_lighting_rgblight_get_config_t ret; + + ret.enable = rgblight_config.enable; + ret.mode = rgblight2xap(rgblight_config.mode); + ret.hue = rgblight_config.hue; + ret.sat = rgblight_config.sat; + ret.val = rgblight_config.val; + ret.speed = rgblight_config.speed; + + return xap_respond_data(token, &ret, sizeof(ret)); +} + +bool xap_respond_set_rgblight_config(xap_token_t token, const void *data, size_t length) { + if (length != sizeof(xap_route_lighting_rgblight_set_config_arg_t)) { + return false; + } + + xap_route_lighting_rgblight_set_config_arg_t *arg = (xap_route_lighting_rgblight_set_config_arg_t *)data; + + uint8_t mode = xap2rgblight(arg->mode); + if (mode == INVALID_EFFECT) { + return false; + } + + rgblight_enabled_noeeprom(arg->enable); + rgblight_mode_noeeprom(mode); + rgblight_sethsv_noeeprom(arg->hue, arg->sat, arg->val); + rgblight_set_speed_noeeprom(arg->speed); + + return xap_respond_success(token); +} + +bool xap_respond_save_rgblight_config(xap_token_t token, const void *data, size_t length) { + eeconfig_update_rgblight_current(); + + return xap_respond_success(token); +} +#endif + +#if ((defined(RGB_MATRIX_ENABLE))) +# include "rgb_matrix.h" + +extern rgb_config_t rgb_matrix_config; + +uint8_t rgb_matrix2xap(uint8_t val); +uint8_t xap2rgb_matrix(uint8_t val); + +void rgb_matrix_enabled_noeeprom(bool val) { + val ? rgb_matrix_enable_noeeprom() : rgb_matrix_disable_noeeprom(); +} + +bool xap_respond_get_rgb_matrix_config(xap_token_t token, const void *data, size_t length) { + xap_route_lighting_rgb_matrix_get_config_t ret; + + ret.enable = rgb_matrix_config.enable; + ret.mode = rgb_matrix2xap(rgb_matrix_config.mode); + ret.hue = rgb_matrix_config.hsv.h; + ret.sat = rgb_matrix_config.hsv.s; + ret.val = rgb_matrix_config.hsv.v; + ret.speed = rgb_matrix_config.speed; + ret.flags = rgb_matrix_config.flags; + + return xap_respond_data(token, &ret, sizeof(ret)); +} + +bool xap_respond_set_rgb_matrix_config(xap_token_t token, const void *data, size_t length) { + if (length != sizeof(xap_route_lighting_rgb_matrix_set_config_arg_t)) { + return false; + } + + xap_route_lighting_rgb_matrix_set_config_arg_t *arg = (xap_route_lighting_rgb_matrix_set_config_arg_t *)data; + + uint8_t mode = xap2rgb_matrix(arg->mode); + if (mode == INVALID_EFFECT) { + return false; + } + + rgb_matrix_enabled_noeeprom(arg->enable); + rgb_matrix_mode_noeeprom(mode); + rgb_matrix_sethsv_noeeprom(arg->hue, arg->sat, arg->val); + rgb_matrix_set_speed_noeeprom(arg->speed); + rgb_matrix_set_flags_noeeprom(arg->flags); + + return xap_respond_success(token); +} + +bool xap_respond_save_rgb_matrix_config(xap_token_t token, const void *data, size_t length) { + eeconfig_update_rgb_matrix(); + + return xap_respond_success(token); +} +#endif diff --git a/quantum/xap/handlers/remapping.c b/quantum/xap/handlers/remapping.c new file mode 100644 index 000000000000..6378dfc80c2e --- /dev/null +++ b/quantum/xap/handlers/remapping.c @@ -0,0 +1,82 @@ +// Copyright 2022 QMK +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "quantum.h" +#include "xap.h" + +#ifdef DYNAMIC_KEYMAP_ENABLE +# define keymap_max_layer_count() DYNAMIC_KEYMAP_LAYER_COUNT +#else +# define keymap_max_layer_count() keymap_layer_count() +#endif + +bool xap_respond_keymap_get_layer_count(xap_token_t token, const void *data, size_t length) { + uint8_t ret = keymap_max_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; + + if (arg->layer >= keymap_max_layer_count()) { + return false; + } + + uint16_t keycode = keycode_at_keymap_location(arg->layer, arg->row, arg->column); + 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; + + if (arg->layer >= keymap_max_layer_count()) { + return false; + } + + uint16_t keycode = keycode_at_encodermap_location(arg->layer, arg->encoder, arg->clockwise); + 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; + + if (arg->layer >= keymap_max_layer_count()) { + return false; + } + + dynamic_keymap_set_keycode(arg->layer, arg->row, arg->column, arg->keycode); + return xap_respond_success(token); +} +#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; + + if (arg->layer >= keymap_max_layer_count()) { + return false; + } + + dynamic_keymap_set_encoder(arg->layer, arg->encoder, arg->clockwise, arg->keycode); + return xap_respond_success(token); +} +#endif \ No newline at end of file diff --git a/quantum/xap/xap_handlers.c b/quantum/xap/xap_handlers.c index 625261af71a3..109c15d54983 100644 --- a/quantum/xap/xap_handlers.c +++ b/quantum/xap/xap_handlers.c @@ -15,10 +15,9 @@ */ #include -#include -#include +#include "quantum.h" +#include "xap.h" -#include "hardware_id.h" #include "secure.h" #ifndef SECURE_ENABLE # define secure_get_status() SECURE_UNLOCKED @@ -26,12 +25,6 @@ # define secure_lock() #endif -#ifdef DYNAMIC_KEYMAP_ENABLE -# define keymap_max_layer_count() DYNAMIC_KEYMAP_LAYER_COUNT -#else -# define keymap_max_layer_count() keymap_layer_count() -#endif - bool xap_respond_success(xap_token_t token) { xap_send(token, XAP_RESPONSE_FLAG_SUCCESS, NULL, 0); return true; @@ -57,288 +50,7 @@ bool xap_respond_u32(xap_token_t token, uint32_t value) { return true; } -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; - memcpy(&offset, data, sizeof(uint16_t)); - - xap_route_qmk_config_blob_chunk_t ret = {0}; - - bool get_config_blob_chunk(uint16_t offset, uint8_t * data, uint8_t data_len); - if (!get_config_blob_chunk(offset, (uint8_t *)&ret, sizeof(ret))) { - return false; - } - - 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_success(token); -} - -bool xap_respond_secure_lock(xap_token_t token, const void *data, size_t length) { - secure_lock(); - return xap_respond_success(token); -} - -#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 - -#ifndef NO_RESET -bool xap_respond_request_eeprom_reset(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)); - eeconfig_disable(); - soft_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_max_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; - - if (arg->layer >= keymap_max_layer_count()) { - return false; - } - - uint16_t keycode = keycode_at_keymap_location(arg->layer, arg->row, arg->column); - 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; - - if (arg->layer >= keymap_max_layer_count()) { - return false; - } - - uint16_t keycode = keycode_at_encodermap_location(arg->layer, arg->encoder, arg->clockwise); - 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; - - if (arg->layer >= keymap_max_layer_count()) { - return false; - } - - dynamic_keymap_set_keycode(arg->layer, arg->row, arg->column, arg->keycode); - return xap_respond_success(token); -} -#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; - - if (arg->layer >= keymap_max_layer_count()) { - return false; - } - - dynamic_keymap_set_encoder(arg->layer, arg->encoder, arg->clockwise, arg->keycode); - return xap_respond_success(token); -} -#endif - -#define INVALID_EFFECT 0xFF - -#if ((defined(BACKLIGHT_ENABLE))) -# include "backlight.h" - -extern backlight_config_t backlight_config; - -bool xap_respond_get_backlight_config(xap_token_t token, const void *data, size_t length) { - xap_route_lighting_backlight_get_config_t ret; - - ret.enable = backlight_config.enable; - ret.mode = backlight_config.breathing; - ret.val = backlight_config.level; - - return xap_respond_data(token, &ret, sizeof(ret)); -} - -bool xap_respond_set_backlight_config(xap_token_t token, const void *data, size_t length) { - if (length != sizeof(xap_route_lighting_backlight_set_config_arg_t)) { - return false; - } - - xap_route_lighting_backlight_set_config_arg_t *arg = (xap_route_lighting_backlight_set_config_arg_t *)data; - - if (arg->enable) { - backlight_level_noeeprom(arg->val); - } else { - backlight_level_noeeprom(0); - } - -# ifdef BACKLIGHT_BREATHING - if (arg->mode) { - backlight_enable_breathing(); - } else { - backlight_disable_breathing(); - } -# endif - - return xap_respond_success(token); -} - -bool xap_respond_save_backlight_config(xap_token_t token, const void *data, size_t length) { - eeconfig_update_backlight_current(); - - return xap_respond_success(token); -} -#endif - -#if ((defined(RGBLIGHT_ENABLE))) -# include "rgblight.h" - -extern rgblight_config_t rgblight_config; - -uint8_t rgblight2xap(uint8_t val); -uint8_t xap2rgblight(uint8_t val); - -void rgblight_enabled_noeeprom(bool val) { - val ? rgblight_enable_noeeprom() : rgblight_disable_noeeprom(); -} - -bool xap_respond_get_rgblight_config(xap_token_t token, const void *data, size_t length) { - xap_route_lighting_rgblight_get_config_t ret; - - ret.enable = rgblight_config.enable; - ret.mode = rgblight2xap(rgblight_config.mode); - ret.hue = rgblight_config.hue; - ret.sat = rgblight_config.sat; - ret.val = rgblight_config.val; - ret.speed = rgblight_config.speed; - - return xap_respond_data(token, &ret, sizeof(ret)); -} - -bool xap_respond_set_rgblight_config(xap_token_t token, const void *data, size_t length) { - if (length != sizeof(xap_route_lighting_rgblight_set_config_arg_t)) { - return false; - } - - xap_route_lighting_rgblight_set_config_arg_t *arg = (xap_route_lighting_rgblight_set_config_arg_t *)data; - - uint8_t mode = xap2rgblight(arg->mode); - if (mode == INVALID_EFFECT) { - return false; - } - - rgblight_enabled_noeeprom(arg->enable); - rgblight_mode_noeeprom(mode); - rgblight_sethsv_noeeprom(arg->hue, arg->sat, arg->val); - rgblight_set_speed_noeeprom(arg->speed); - - return xap_respond_success(token); -} - -bool xap_respond_save_rgblight_config(xap_token_t token, const void *data, size_t length) { - eeconfig_update_rgblight_current(); - - return xap_respond_success(token); -} -#endif - -#if ((defined(RGB_MATRIX_ENABLE))) -# include "rgb_matrix.h" - -extern rgb_config_t rgb_matrix_config; - -uint8_t rgb_matrix2xap(uint8_t val); -uint8_t xap2rgb_matrix(uint8_t val); - -void rgb_matrix_enabled_noeeprom(bool val) { - val ? rgb_matrix_enable_noeeprom() : rgb_matrix_disable_noeeprom(); -} - -bool xap_respond_get_rgb_matrix_config(xap_token_t token, const void *data, size_t length) { - xap_route_lighting_rgb_matrix_get_config_t ret; - - ret.enable = rgb_matrix_config.enable; - ret.mode = rgb_matrix2xap(rgb_matrix_config.mode); - ret.hue = rgb_matrix_config.hsv.h; - ret.sat = rgb_matrix_config.hsv.s; - ret.val = rgb_matrix_config.hsv.v; - ret.speed = rgb_matrix_config.speed; - ret.flags = rgb_matrix_config.flags; - - return xap_respond_data(token, &ret, sizeof(ret)); -} - -bool xap_respond_set_rgb_matrix_config(xap_token_t token, const void *data, size_t length) { - if (length != sizeof(xap_route_lighting_rgb_matrix_set_config_arg_t)) { - return false; - } - - xap_route_lighting_rgb_matrix_set_config_arg_t *arg = (xap_route_lighting_rgb_matrix_set_config_arg_t *)data; - - uint8_t mode = xap2rgb_matrix(arg->mode); - if (mode == INVALID_EFFECT) { - return false; - } - - rgb_matrix_enabled_noeeprom(arg->enable); - rgb_matrix_mode_noeeprom(mode); - rgb_matrix_sethsv_noeeprom(arg->hue, arg->sat, arg->val); - rgb_matrix_set_speed_noeeprom(arg->speed); - rgb_matrix_set_flags_noeeprom(arg->flags); - - return xap_respond_success(token); -} - -bool xap_respond_save_rgb_matrix_config(xap_token_t token, const void *data, size_t length) { - eeconfig_update_rgb_matrix(); - - return xap_respond_success(token); -} -#endif +#include "handlers/core.c" +#include "handlers/remapping.c" +#include "handlers/lighting.c" +#include "handlers/audio.c" From 35ff477d771ae981d1f5e3c370453ae96130ba00 Mon Sep 17 00:00:00 2001 From: zvecr Date: Sun, 1 Jan 2023 19:20:04 +0000 Subject: [PATCH 168/203] dedupe merge_ordered_dicts --- lib/python/qmk/xap/common.py | 41 ++++-------------------------------- 1 file changed, 4 insertions(+), 37 deletions(-) diff --git a/lib/python/qmk/xap/common.py b/lib/python/qmk/xap/common.py index fcbb4be43006..c7176fe69b45 100755 --- a/lib/python/qmk/xap/common.py +++ b/lib/python/qmk/xap/common.py @@ -10,7 +10,7 @@ import qmk.constants from qmk.git import git_get_version -from qmk.json_schema import json_load, validate +from qmk.json_schema import json_load, validate, merge_ordered_dicts from qmk.decorators import lru_cache from qmk.keymap import locate_keymap from qmk.path import keyboard @@ -94,39 +94,6 @@ 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 /data/xap. """ @@ -144,7 +111,7 @@ def update_xap_definitions(original, new): """ if original is None: original = OrderedDict() - return _merge_ordered_dicts([original, new]) + return merge_ordered_dicts([original, new]) @lru_cache(timeout=5) @@ -159,7 +126,7 @@ def get_xap_defs(version): files = files[:(index + 1)] definitions = [hjson.load(file.open(encoding='utf-8')) for file in files] - return _merge_ordered_dicts(definitions) + return merge_ordered_dicts(definitions) def latest_xap_defs(): @@ -181,7 +148,7 @@ def merge_xap_defs(kb, km): if km_xap.exists(): definitions.append({'routes': {'0x03': hjson.load(km_xap.open(encoding='utf-8'))}}) - defs = _merge_ordered_dicts(definitions) + defs = merge_ordered_dicts(definitions) try: validate(defs, 'qmk.xap.v1') From 8c3e1feca71d31558ed91b1a0e9bc25e44c6e843 Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 4 Jan 2023 04:59:12 +0000 Subject: [PATCH 169/203] regen_push - install dev deps --- .github/workflows/regen_push.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/regen_push.yml b/.github/workflows/regen_push.yml index 37b26d980c81..4968f91445e7 100644 --- a/.github/workflows/regen_push.yml +++ b/.github/workflows/regen_push.yml @@ -18,6 +18,10 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Install dependencies + run: | + pip3 install -r requirements-dev.txt + - name: Run qmk generators run: | util/regen.sh From 8829d0431c0b04bd1797db823e87dff71e739824 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Thu, 5 Jan 2023 11:08:45 +1100 Subject: [PATCH 170/203] Reset macros as well. --- quantum/eeconfig.c | 2 ++ quantum/keymap_introspection.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/quantum/eeconfig.c b/quantum/eeconfig.c index 50f9a9b22f8e..32c79526db16 100644 --- a/quantum/eeconfig.c +++ b/quantum/eeconfig.c @@ -20,6 +20,7 @@ void eeconfig_init_via(void); #elif defined(DYNAMIC_KEYMAP_ENABLE) bool dynamic_keymap_is_valid(void); void dynamic_keymap_reset(void); +void dynamic_keymap_macro_reset(void); #endif /** \brief eeconfig enable @@ -89,6 +90,7 @@ void eeconfig_init_quantum(void) { eeconfig_init_via(); #elif defined(DYNAMIC_KEYMAP_ENABLE) dynamic_keymap_reset(); + dynamic_keymap_macro_reset(); #endif eeconfig_init_kb(); diff --git a/quantum/keymap_introspection.c b/quantum/keymap_introspection.c index 1529ab9fe2d7..1f4da2d975c0 100644 --- a/quantum/keymap_introspection.c +++ b/quantum/keymap_introspection.c @@ -27,7 +27,7 @@ uint16_t keycode_at_keymap_location_raw(uint8_t layer_num, uint8_t row, uint8_t if (layer_num < NUM_KEYMAP_LAYERS && row < MATRIX_ROWS && column < MATRIX_COLS) { return pgm_read_word(&keymaps[layer_num][row][column]); } - return KC_NO; + return KC_TRANSPARENT; } __attribute__((weak)) uint16_t keycode_at_keymap_location(uint8_t layer_num, uint8_t row, uint8_t column) { @@ -48,7 +48,7 @@ uint16_t keycode_at_encodermap_location_raw(uint8_t layer_num, uint8_t encoder_i if (layer_num < NUM_ENCODERMAP_LAYERS && encoder_idx < NUM_ENCODERS) { return pgm_read_word(&encoder_map[layer_num][encoder_idx][clockwise ? 0 : 1]); } - return KC_NO; + return KC_TRANSPARENT; } __attribute__((weak)) uint16_t keycode_at_encodermap_location(uint8_t layer_num, uint8_t encoder_idx, bool clockwise) { From 21db0e4f84d9fe2a410634dd704ab5deb440b061 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Thu, 5 Jan 2023 11:39:23 +1100 Subject: [PATCH 171/203] Install python reqs during regen --- .github/workflows/regen.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/regen.yml b/.github/workflows/regen.yml index ae25bc095ee9..9476b03455ed 100644 --- a/.github/workflows/regen.yml +++ b/.github/workflows/regen.yml @@ -18,6 +18,10 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Install python reqs + run: | + python3 -m pip install -r requirements.txt + - name: Run qmk generators run: | util/regen.sh From 8e9e6c3ea70160b9474ceba6ec4e41d4ed5b4d7b Mon Sep 17 00:00:00 2001 From: zvecr Date: Mon, 9 Jan 2023 19:35:47 +0000 Subject: [PATCH 172/203] Handle userspace xap.hjson spec files --- builddefs/xap.mk | 3 +++ lib/python/qmk/xap/common.py | 19 ++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/builddefs/xap.mk b/builddefs/xap.mk index 14c3ada9f4be..03c3aad1e1dd 100644 --- a/builddefs/xap.mk +++ b/builddefs/xap.mk @@ -20,6 +20,9 @@ endif ifneq ("$(wildcard $(KEYMAP_PATH)/xap.hjson)","") XAP_FILES += $(KEYMAP_PATH)/xap.hjson endif +ifneq ("$(wildcard $(USER_NAME)/xap.hjson)","") + XAP_FILES += $(USER_NAME)/xap.hjson +endif $(KEYMAP_OUTPUT)/src/config_blob_gz.h: $(INFO_JSON_FILES) @$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD) diff --git a/lib/python/qmk/xap/common.py b/lib/python/qmk/xap/common.py index c7176fe69b45..e44e2b6643ec 100755 --- a/lib/python/qmk/xap/common.py +++ b/lib/python/qmk/xap/common.py @@ -11,11 +11,13 @@ import qmk.constants from qmk.git import git_get_version from qmk.json_schema import json_load, validate, merge_ordered_dicts +from qmk.makefile import parse_rules_mk_file from qmk.decorators import lru_cache from qmk.keymap import locate_keymap from qmk.path import keyboard from qmk.xap.jinja2_filters import attach_filters +USERSPACE_DIR = Path('users') XAP_SPEC = 'xap.hjson' @@ -91,7 +93,22 @@ def _find_kb_spec(kb): def _find_km_spec(kb, km): - return locate_keymap(kb, km).parent / XAP_SPEC + keymap_dir = locate_keymap(kb, km).parent + if not keymap_dir.exists(): + return None + + # Resolve any potential USER_NAME overrides - default back to keymap name + keymap_rules_mk = parse_rules_mk_file(keymap_dir / 'rules.mk') + username = keymap_rules_mk.get('USER_NAME', km) + + keymap_spec = keymap_dir / XAP_SPEC + userspace_spec = USERSPACE_DIR / username / XAP_SPEC + + print(keymap_spec.exists()) + print(userspace_spec.exists()) + + # In the case of both userspace and keymap - keymap wins + return keymap_spec if keymap_spec.exists() else userspace_spec def get_xap_definition_files(): From 0137c4a3105789fa5ff96756b3dad3cba50fc852 Mon Sep 17 00:00:00 2001 From: zvecr Date: Mon, 9 Jan 2023 19:39:58 +0000 Subject: [PATCH 173/203] Handle userspace xap.hjson spec files - remove prints.... --- lib/python/qmk/xap/common.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/python/qmk/xap/common.py b/lib/python/qmk/xap/common.py index e44e2b6643ec..82e1887629a4 100755 --- a/lib/python/qmk/xap/common.py +++ b/lib/python/qmk/xap/common.py @@ -104,9 +104,6 @@ def _find_km_spec(kb, km): keymap_spec = keymap_dir / XAP_SPEC userspace_spec = USERSPACE_DIR / username / XAP_SPEC - print(keymap_spec.exists()) - print(userspace_spec.exists()) - # In the case of both userspace and keymap - keymap wins return keymap_spec if keymap_spec.exists() else userspace_spec From 03c47d295e840e72f58d97ed37925da5a573b21f Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Fri, 6 Jan 2023 13:49:37 +1100 Subject: [PATCH 174/203] Jinja2 cleanup. --- .../templates/xap/firmware/xap_generated.h.j2 | 129 +++++++++--------- .../xap/firmware/xap_generated.inl.j2 | 30 +++- lib/python/qmk/cli/xap/generate_qmk.py | 2 +- lib/python/qmk/xap/common.py | 2 +- 4 files changed, 95 insertions(+), 68 deletions(-) diff --git a/data/templates/xap/firmware/xap_generated.h.j2 b/data/templates/xap/firmware/xap_generated.h.j2 index efb238bae3d1..167c8776e9be 100755 --- a/data/templates/xap/firmware/xap_generated.h.j2 +++ b/data/templates/xap/firmware/xap_generated.h.j2 @@ -3,6 +3,7 @@ #pragma once #include +#include //////////////////////////////////////////////////////////////////////////////// // Versions and identifiers @@ -15,119 +16,119 @@ //////////////////////////////////////////////////////////////////////////////// // Response flag definitions -{% for bit,data in xap.response_flags.bits | dictsort -%} +{% for bit,data in xap.response_flags.bits | dictsort %} #define {{ xap.response_flags.define_prefix }}_{{ data.define | to_snake | upper }} (UINT32_C(1) << ({{ bit }})) -{% endfor -%} +{% endfor %} #define {{ xap.response_flags.define_prefix }}_FAILED 0x00 //////////////////////////////////////////////////////////////////////////////// // Broadcast message definitions -{% for message_id,data in xap.broadcast_messages.messages | dictsort -%} +{% for message_id,data in xap.broadcast_messages.messages | dictsort %} #define {{ xap.broadcast_messages.define_prefix }}_{{ data.define | to_snake | upper }} {{ message_id }} -{% if 'return_type' in data -%} +{% if 'return_type' in data %} void {{ xap.broadcast_messages.define_prefix | lower }}_{{ data.define | to_snake | lower }}({{ data.return_type | type_to_c('value') }}); -{% else -%} +{% else %} void {{ xap.broadcast_messages.define_prefix | lower }}_{{ data.define | to_snake | lower }}(const void *data, size_t length); {% endif %} -{% endfor -%} +{% endfor %} #define XAP_BROADCAST_TOKEN 0xFFFF //////////////////////////////////////////////////////////////////////////////// // Type definitions -{% for name,data in xap.type_definitions | dictsort -%} -{% if data.type != 'struct' -%} +{% for name,data in xap.type_definitions | dictsort %} +{% if data.type != 'struct' %} typedef {{ data.type | type_to_c('xap_'+(name|to_snake|lower)+'_t') }}; -{% endif -%} +{% endif %} {% endfor %} -{%- for name,data in xap.type_definitions | dictsort %} -{% if data.type == 'struct' -%} +{% for name,data in xap.type_definitions | dictsort %} +{% if data.type == 'struct' %} typedef struct { -{%- for member in data.struct_members %} +{% for member in data.struct_members %} {{ member.type | type_to_c(member.name) }}; -{%- endfor %} +{% endfor %} } __attribute__((__packed__)) xap_{{ name | to_snake | lower }}_t{{ data.type | type_to_c_after }}; _Static_assert(sizeof(xap_{{ name | to_snake | lower }}_t) == {{ data.struct_length }}, "xap_{{ name | to_snake | lower }}_t needs to be {{ data.struct_length }} bytes in size"); -{%- endif -%} +{% endif %} {% endfor %} //////////////////////////////////////////////////////////////////////////////// // Route definitions -{% macro export_route_types(prefix, container) -%} -{%- if 'routes' in container -%} -{% for route, data in container.routes | dictsort -%} -{%- set this_prefix_uc = (prefix + '_' + data.define) | upper -%} -{%- set this_prefix_lc = this_prefix_uc | lower -%} +{% macro export_route_types(prefix, container) %} +{% if 'routes' in container %} +{% for route, data in container.routes | dictsort %} +{% set this_prefix_uc = (prefix + '_' + data.define) | upper %} +{% set this_prefix_lc = this_prefix_uc | lower %} -{% if 'request_struct_members' in data -%} +{% if 'request_struct_members' in data %} typedef struct { -{%- for member in data.request_struct_members %} +{% for member in data.request_struct_members %} {{ member.type | type_to_c(member.name|lower) }}; -{%- endfor %} +{% endfor %} } __attribute__((__packed__)) {{ this_prefix_lc | to_snake | lower }}_arg_t; _Static_assert(sizeof({{ this_prefix_lc | to_snake | lower }}_arg_t) == {{ data.request_struct_length }}, "{{ this_prefix_lc | to_snake | lower }}_arg_t needs to be {{ data.request_struct_length }} bytes in size"); -{% elif 'request_type' in data -%} +{% elif 'request_type' in data %} typedef {{ data.request_type | type_to_c(this_prefix_lc+'_arg_t') }}; -{%- endif -%} +{% endif %} -{%- if 'return_struct_members' in data -%} +{% if 'return_struct_members' in data %} typedef struct { -{%- for member in data.return_struct_members %} +{% for member in data.return_struct_members %} {{ member.type | type_to_c(member.name|lower) }}; -{%- endfor %} +{% endfor %} } __attribute__((__packed__)) {{ this_prefix_lc | to_snake | lower }}_t; _Static_assert(sizeof({{ this_prefix_lc | to_snake | lower }}_t) == {{ data.return_struct_length }}, "{{ this_prefix_lc | to_snake | lower }}_t needs to be {{ data.return_struct_length }} bytes in size"); -{%- elif 'return_type' in data -%} -{%- if '[' in data.return_type %} +{% elif 'return_type' in data %} +{% if '[' in data.return_type %} typedef struct __attribute__((__packed__)) { {{ data.return_type | type_to_c('x') }}; } {{ this_prefix_lc }}_t; -{%- else -%} +{% else %} typedef {{ data.return_type | type_to_c(this_prefix_lc+'_t') }}; -{%- endif -%} +{% endif %} -{%- endif %} +{% endif %} {{ export_route_types(this_prefix_lc, data) }} -{% endfor -%} -{%- endif -%} -{%- endmacro -%} +{% endfor %} +{% endif %} +{% endmacro %} {{ export_route_types('xap_route', xap) }} //////////////////////////////////////////////////////////////////////////////// // Capabilities IDs -{% macro export_route_ids(prefix, container) -%} -{%- if 'routes' in container -%} -{% for route, data in container.routes | dictsort -%} -{%- set this_prefix_uc = (prefix + '_' + data.define) | upper -%} -{%- set this_prefix_lc = this_prefix_uc | lower -%} +{% macro export_route_ids(prefix, container) %} +{% if 'routes' in container %} +{% for route, data in container.routes | dictsort %} +{% set this_prefix_uc = (prefix + '_' + data.define) | upper %} +{% set this_prefix_lc = this_prefix_uc | lower %} #define {{ this_prefix_uc }} {{ route }} {{ export_route_ids(this_prefix_uc, data) }} -{%- endfor -%} -{%- endif -%} -{%- endmacro -%} +{% endfor %} +{% endif %} +{% endmacro %} {{ export_route_ids('XAP_ROUTE', xap) }} //////////////////////////////////////////////////////////////////////////////// // Capabilities Masks -{% macro export_route_masks(prefix, container, preprocessor_condition) -%} -{%- if 'routes' in container -%} -{% for route, data in container.routes | dictsort -%} -{%- set this_prefix_uc = (prefix + '_' + data.define) | upper -%} -{%- set this_prefix_lc = this_prefix_uc | lower -%} +{% macro export_route_masks(prefix, container, preprocessor_condition) %} +{% if 'routes' in container %} +{% for route, data in container.routes | dictsort %} +{% set this_prefix_uc = (prefix + '_' + data.define) | upper %} +{% set this_prefix_lc = this_prefix_uc | lower %} {% if 'enable_if_preprocessor' in data %} {% if preprocessor_condition == 'TRUE' %} -{%- set condition = "(" + data.enable_if_preprocessor + ")" -%} +{% set condition = "(" + data.enable_if_preprocessor + ")" %} {% else %} -{%- set condition = "(" + preprocessor_condition + " && (" + data.enable_if_preprocessor + "))" -%} +{% set condition = "(" + preprocessor_condition + " && (" + data.enable_if_preprocessor + "))" %} {% endif %} {% else %} -{%- set condition = preprocessor_condition -%} +{% set condition = preprocessor_condition %} {% endif %} {% if condition == 'TRUE' %} #define {{ this_prefix_uc }}_MASK (UINT32_C(1) << ({{ this_prefix_uc }})) @@ -139,28 +140,28 @@ typedef {{ data.return_type | type_to_c(this_prefix_lc+'_t') }}; #endif // ({{ condition }}) {% endif %} {{ export_route_masks(this_prefix_uc, data, condition) }} -{%- endfor -%} -{%- endif -%} -{%- endmacro -%} +{% endfor %} +{% endif %} +{% endmacro %} {{ export_route_masks('XAP_ROUTE', xap, 'TRUE') }} //////////////////////////////////////////////////////////////////////////////// // Capabilities Values -{% macro export_route_capabilities(prefix, container) -%} -{%- if 'routes' in container -%} +{% macro export_route_capabilities(prefix, container) %} +{% if 'routes' in container %} #define {{ prefix }}_CAPABILITIES (0 \ -{% for route, data in container.routes | dictsort -%} -{%- set this_prefix_uc = (prefix + '_' + data.define) | upper -%} +{% for route, data in container.routes | dictsort %} +{% set this_prefix_uc = (prefix + '_' + data.define) | upper %} | ({{ this_prefix_uc }}_MASK) \ -{% endfor -%} +{% endfor %} ) -{% for route, data in container.routes | dictsort -%} -{%- set this_prefix_uc = (prefix + '_' + data.define) | upper -%} +{% for route, data in container.routes | dictsort %} +{% set this_prefix_uc = (prefix + '_' + data.define) | upper %} {{ export_route_capabilities(this_prefix_uc, data) }} -{% endfor -%} -{%- endif -%} -{%- endmacro -%} +{% endfor %} +{% endif %} +{% endmacro %} {{ export_route_capabilities('XAP_ROUTE', xap) }} diff --git a/data/templates/xap/firmware/xap_generated.inl.j2 b/data/templates/xap/firmware/xap_generated.inl.j2 index 2f30ffcac727..79359fb315ca 100755 --- a/data/templates/xap/firmware/xap_generated.inl.j2 +++ b/data/templates/xap/firmware/xap_generated.inl.j2 @@ -4,6 +4,32 @@ //////////////////////////////////////////////////////////////////////////////// // Full XAP {{ xap.version }} definitions +{% for message_id,data in xap.broadcast_messages.messages | dictsort %} +{% if 'return_type' in data %} +void {{ xap.broadcast_messages.define_prefix | lower }}_{{ data.define | to_snake | lower }}({{ data.return_type | type_to_c('value') }}) { xap_broadcast({{ message_id }}, &value, sizeof(value)); } +{% else %} +void {{ xap.broadcast_messages.define_prefix | lower }}_{{ data.define | to_snake | lower }}(const void *data, size_t length) { xap_broadcast({{ message_id }}, data, length); } +{% endif %} +{% endfor %} + +{% macro append_routing_table(prefix, container, route_stack) %} +{% set this_route_stack = route_stack.copy() %} +{{ this_route_stack.append(container) }} +{% set stack_names = this_route_stack | map(attribute='name') | join(', ') %} +Stack names: {{ stack_names }} +{% if 'routes' in container %} +{% for route, data in container.routes | dictsort %} +{% set this_prefix_uc = (prefix + '_' + data.define) | upper %} +{% set this_prefix_lc = this_prefix_uc | lower %} +{{ append_routing_table(this_prefix_lc, data, this_route_stack) }} +Inner route prefix for {{ prefix }}: {{ this_prefix_lc }} +{% endfor %} +{% endif %} +Prefix: {{ prefix }} +{% endmacro %} + +{{ append_routing_table("xap_route", xap, []) }} + #if 0 -{{ xap | tojson(4) }} -#endif +{{ xap | tojson(indent=4) }} +#endif \ No newline at end of file diff --git a/lib/python/qmk/cli/xap/generate_qmk.py b/lib/python/qmk/cli/xap/generate_qmk.py index fd6ef52b97eb..06ec8a0bf1be 100755 --- a/lib/python/qmk/cli/xap/generate_qmk.py +++ b/lib/python/qmk/cli/xap/generate_qmk.py @@ -29,7 +29,7 @@ def xap_generate_qmk_inc(cli): generate_inline(cli.args.output, cli.args.keyboard, cli.args.keymap) defs = merge_xap_defs(cli.args.keyboard, cli.args.keymap) - with open(normpath(str(cli.args.output.resolve()) + '.generated.j2'), 'w', encoding='utf-8') as out_file: + with open(normpath(str(cli.args.output.resolve()) + '.generated.j2.c'), 'w', encoding='utf-8') as out_file: r = render_xap_output('firmware', 'xap_generated.inl.j2', defs, keyboard=cli.args.keyboard, keymap=cli.args.keymap) while r.find('\n\n\n') != -1: r = r.replace('\n\n\n', '\n\n') diff --git a/lib/python/qmk/xap/common.py b/lib/python/qmk/xap/common.py index 82e1887629a4..9f84ab8a02fa 100755 --- a/lib/python/qmk/xap/common.py +++ b/lib/python/qmk/xap/common.py @@ -56,7 +56,7 @@ def load_lighting_spec(feature, version='latest'): def _get_jinja2_env(data_templates_xap_subdir: str): templates_dir = os.path.join(qmk.constants.QMK_FIRMWARE, 'data', 'templates', 'xap', data_templates_xap_subdir) - j2 = Environment(loader=FileSystemLoader(templates_dir), autoescape=select_autoescape()) + j2 = Environment(loader=FileSystemLoader(templates_dir), autoescape=select_autoescape(), lstrip_blocks=True, trim_blocks=True) return j2 From 2f7be506de354bb4ace73f3cd27cf365a9e48fd8 Mon Sep 17 00:00:00 2001 From: zvecr Date: Mon, 16 Jan 2023 02:03:41 +0000 Subject: [PATCH 175/203] Remove assumption on XAP interface number --- tmk_core/protocol/chibios/usb_main.c | 22 ++-- tmk_core/protocol/lufa/lufa.c | 12 +- tmk_core/protocol/usb_descriptor.c | 170 +++++++++++++------------ tmk_core/protocol/usb_descriptor.h | 42 +++--- tmk_core/protocol/vusb/vusb.c | 184 +++++++++++++-------------- tmk_core/protocol/vusb/vusb.h | 14 +- 6 files changed, 223 insertions(+), 221 deletions(-) diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index 29f340268aa7..75a6d9b91b4a 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -368,14 +368,14 @@ 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 #ifdef VIRTSER_ENABLE usb_driver_config_t serial_driver; +#endif +#ifdef XAP_ENABLE + usb_driver_config_t xap_driver; #endif }; usb_driver_config_t array[0]; @@ -402,14 +402,6 @@ 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 @@ -425,6 +417,14 @@ static usb_driver_configs_t drivers = { # define CDC_OUT_MODE USB_EP_MODE_TYPE_BULK .serial_driver = QMK_USB_DRIVER_CONFIG(CDC, CDC_NOTIFICATION_EPNUM, 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 }; #define NUM_USB_DRIVERS (sizeof(drivers) / sizeof(usb_driver_config_t)) diff --git a/tmk_core/protocol/lufa/lufa.c b/tmk_core/protocol/lufa/lufa.c index f050c5ac2075..3d9bb94f4e8a 100644 --- a/tmk_core/protocol/lufa/lufa.c +++ b/tmk_core/protocol/lufa/lufa.c @@ -486,12 +486,6 @@ 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); @@ -523,6 +517,12 @@ void EVENT_USB_Device_ConfigurationChanged(void) { ConfigSuccess &= Endpoint_ConfigureEndpoint((DIGITIZER_IN_EPNUM | ENDPOINT_DIR_IN), EP_TYPE_INTERRUPT, DIGITIZER_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 + usb_device_state_set_configuration(USB_DeviceState == DEVICE_STATE_Configured, USB_Device_ConfigurationNumber); } diff --git a/tmk_core/protocol/usb_descriptor.c b/tmk_core/protocol/usb_descriptor.c index 9c104fe43403..87f18267acb5 100644 --- a/tmk_core/protocol/usb_descriptor.c +++ b/tmk_core/protocol/usb_descriptor.c @@ -406,53 +406,53 @@ 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') +#ifdef CONSOLE_ENABLE +const USB_Descriptor_HIDReport_Datatype_t PROGMEM ConsoleReport[] = { + HID_RI_USAGE_PAGE(16, 0xFF31), // Vendor Defined (PJRC Teensy compatible) + HID_RI_USAGE(8, 0x74), // Vendor Defined (PJRC Teensy compatible) HID_RI_COLLECTION(8, 0x01), // Application // Data to host - HID_RI_USAGE(8, 0x62), // Vendor Defined + HID_RI_USAGE(8, 0x75), // Vendor Defined HID_RI_LOGICAL_MINIMUM(8, 0x00), HID_RI_LOGICAL_MAXIMUM(16, 0x00FF), - HID_RI_REPORT_COUNT(8, XAP_EPSIZE), + HID_RI_REPORT_COUNT(8, CONSOLE_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_USAGE(8, 0x76), // Vendor Defined HID_RI_LOGICAL_MINIMUM(8, 0x00), HID_RI_LOGICAL_MAXIMUM(16, 0x00FF), - HID_RI_REPORT_COUNT(8, XAP_EPSIZE), + HID_RI_REPORT_COUNT(8, CONSOLE_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 +#endif -#ifdef CONSOLE_ENABLE -const USB_Descriptor_HIDReport_Datatype_t PROGMEM ConsoleReport[] = { - HID_RI_USAGE_PAGE(16, 0xFF31), // Vendor Defined (PJRC Teensy compatible) - HID_RI_USAGE(8, 0x74), // Vendor Defined (PJRC Teensy compatible) +#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, 0x75), // Vendor Defined + HID_RI_USAGE(8, 0x62), // Vendor Defined HID_RI_LOGICAL_MINIMUM(8, 0x00), HID_RI_LOGICAL_MAXIMUM(16, 0x00FF), - HID_RI_REPORT_COUNT(8, CONSOLE_EPSIZE), + 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, 0x76), // Vendor Defined + HID_RI_USAGE(8, 0x63), // Vendor Defined HID_RI_LOGICAL_MINIMUM(8, 0x00), HID_RI_LOGICAL_MAXIMUM(16, 0x00FF), - HID_RI_REPORT_COUNT(8, CONSOLE_EPSIZE), + 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 +#endif // XAP_ENABLE /* * Device descriptor @@ -603,56 +603,6 @@ 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 @@ -1122,6 +1072,56 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = { .PollingIntervalMS = USB_POLLING_INTERVAL_MS }, #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 }; /* @@ -1250,14 +1250,6 @@ 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; @@ -1265,12 +1257,14 @@ uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const break; #endif + #if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP) case JOYSTICK_INTERFACE: Address = &ConfigurationDescriptor.Joystick_HID; Size = sizeof(USB_HID_Descriptor_HID_t); break; #endif + #if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP) case DIGITIZER_INTERFACE: Address = &ConfigurationDescriptor.Digitizer_HID; @@ -1278,6 +1272,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 } break; @@ -1315,14 +1317,6 @@ 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; @@ -1342,6 +1336,14 @@ uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const Size = sizeof(DigitizerReport); break; #endif + +#ifdef XAP_ENABLE + case XAP_INTERFACE: + Address = &XapReport; + Size = sizeof(XapReport); + + break; +#endif } break; diff --git a/tmk_core/protocol/usb_descriptor.h b/tmk_core/protocol/usb_descriptor.h index 974352720686..cf91ff3e661b 100644 --- a/tmk_core/protocol/usb_descriptor.h +++ b/tmk_core/protocol/usb_descriptor.h @@ -78,14 +78,6 @@ 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; @@ -153,6 +145,14 @@ typedef struct { USB_HID_Descriptor_HID_t Digitizer_HID; USB_Descriptor_Endpoint_t Digitizer_INEndpoint; #endif + +#ifdef XAP_ENABLE + // XAP 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 } USB_Descriptor_Configuration_t; /* @@ -173,10 +173,6 @@ enum usb_interfaces { RAW_INTERFACE, #endif -#ifdef XAP_ENABLE - XAP_INTERFACE, -#endif - #if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP) MOUSE_INTERFACE, #endif @@ -206,6 +202,10 @@ enum usb_interfaces { #if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP) DIGITIZER_INTERFACE, #endif + +#ifdef XAP_ENABLE + XAP_INTERFACE, +#endif TOTAL_INTERFACES }; @@ -238,15 +238,6 @@ 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 @@ -302,6 +293,15 @@ enum usb_endpoints { # define DIGITIZER_IN_EPNUM SHARED_IN_EPNUM # 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 PROTOCOL_LUFA diff --git a/tmk_core/protocol/vusb/vusb.c b/tmk_core/protocol/vusb/vusb.c index 3ee3f4e5660c..0f221fa2eea4 100644 --- a/tmk_core/protocol/vusb/vusb.c +++ b/tmk_core/protocol/vusb/vusb.c @@ -73,10 +73,6 @@ 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 @@ -85,6 +81,10 @@ enum usb_interfaces { CONSOLE_INTERFACE = NEXT_INTERFACE, #endif +#ifdef XAP_ENABLE + XAP_INTERFACE = NEXT_INTERFACE, +#endif + TOTAL_INTERFACES = NEXT_INTERFACE }; @@ -789,29 +789,6 @@ 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) @@ -835,6 +812,29 @@ const PROGMEM uchar console_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 + #ifndef USB_MAX_POWER_CONSUMPTION # define USB_MAX_POWER_CONSUMPTION 500 #endif @@ -1011,56 +1011,6 @@ 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 @@ -1157,7 +1107,57 @@ const PROGMEM usbConfigurationDescriptor_t usbConfigurationDescriptor = { .bmAttributes = 0x03, .wMaxPacketSize = CONSOLE_EPSIZE, .bInterval = 0x01 - } + }, +# 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 }; @@ -1216,13 +1216,6 @@ 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; @@ -1236,6 +1229,13 @@ USB_PUBLIC usbMsgLen_t usbFunctionDescriptor(struct usbRequest *rq) { len = sizeof(usbHIDDescriptor_t); break; #endif + +#if defined(XAP_ENABLE) + case XAP_INTERFACE: + usbMsgPtr = (usbMsgPtr_t)&usbConfigurationDescriptor.xapHID; + len = sizeof(usbHIDDescriptor_t); + break; +#endif } break; case USBDESCR_HID_REPORT: @@ -1255,13 +1255,6 @@ 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; @@ -1275,6 +1268,13 @@ USB_PUBLIC usbMsgLen_t usbFunctionDescriptor(struct usbRequest *rq) { len = sizeof(console_hid_report); break; #endif + +#if defined(XAP_ENABLE) + case XAP_INTERFACE: + usbMsgPtr = (usbMsgPtr_t)xap_report; + len = sizeof(xap_report); + break; +#endif } break; } diff --git a/tmk_core/protocol/vusb/vusb.h b/tmk_core/protocol/vusb/vusb.h index df4c93d10870..c3435b2084f5 100644 --- a/tmk_core/protocol/vusb/vusb.h +++ b/tmk_core/protocol/vusb/vusb.h @@ -104,13 +104,6 @@ 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; @@ -123,6 +116,13 @@ typedef struct usbConfigurationDescriptor { usbEndpointDescriptor_t consoleINEndpoint; usbEndpointDescriptor_t consoleOUTEndpoint; #endif + +#if defined(XAP_ENABLE) + usbInterfaceDescriptor_t xapInterface; + usbHIDDescriptor_t xapHID; + usbEndpointDescriptor_t xapINEndpoint; + usbEndpointDescriptor_t xapOUTEndpoint; +#endif } __attribute__((packed)) usbConfigurationDescriptor_t; extern bool vusb_suspended; From 76616827870a95b5bb142a3f1ae1b55d8d60b991 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Sun, 19 Feb 2023 11:51:29 +1100 Subject: [PATCH 176/203] PROGMEM led mappings --- lib/python/qmk/cli/xap/generate_lighting.py | 10 +++---- quantum/xap/lighting_map.h | 33 +++++++++++---------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/lib/python/qmk/cli/xap/generate_lighting.py b/lib/python/qmk/cli/xap/generate_lighting.py index 1716056489c9..e19639a65fe6 100644 --- a/lib/python/qmk/cli/xap/generate_lighting.py +++ b/lib/python/qmk/cli/xap/generate_lighting.py @@ -28,7 +28,7 @@ def _append_lighting_map(lines, feature, spec): ifdef_prefix = PREFIX_MAP[feature]['ifdef'] def_prefix = PREFIX_MAP[feature]['def'] - lines.append(f'static uint8_t {feature}_effect_map[][2] = {{') + lines.append(f'static const uint8_t {feature}_effect_map[][2] PROGMEM = {{') for id, obj in spec.get('effects', {}).items(): define = obj['define'] offset = f' + {obj["offset"]}' if obj['offset'] else '' @@ -54,16 +54,16 @@ def _append_lighting_map(lines, feature, spec): f''' uint8_t {feature}2xap(uint8_t val) {{ for(uint8_t i = 0; i < ARRAY_SIZE({feature}_effect_map); i++) {{ - if ({feature}_effect_map[i][1] == val) - return {feature}_effect_map[i][0]; + if (pgm_read_byte(&{feature}_effect_map[i][1]) == val) + return pgm_read_byte(&{feature}_effect_map[i][0]); }} return 0xFF; }} uint8_t xap2{feature}(uint8_t val) {{ for(uint8_t i = 0; i < ARRAY_SIZE({feature}_effect_map); i++) {{ - if ({feature}_effect_map[i][0] == val) - return {feature}_effect_map[i][1]; + if (pgm_read_byte(&{feature}_effect_map[i][0]) == val) + return pgm_read_byte(&{feature}_effect_map[i][1]); }} return 0xFF; }}''' diff --git a/quantum/xap/lighting_map.h b/quantum/xap/lighting_map.h index 420cb7e2b994..f44965ddf092 100644 --- a/quantum/xap/lighting_map.h +++ b/quantum/xap/lighting_map.h @@ -1,4 +1,4 @@ -// Copyright 2022 QMK +// Copyright 2023 QMK // SPDX-License-Identifier: GPL-2.0-or-later /******************************************************************************* @@ -31,7 +31,7 @@ #define ENABLE_LED_MATRIX_SOLID #ifdef RGBLIGHT_ENABLE -static uint8_t rgblight_effect_map[][2] = { +static const uint8_t rgblight_effect_map[][2] PROGMEM = { #ifdef RGBLIGHT_EFFECT_STATIC_LIGHT { 0x00, RGBLIGHT_MODE_STATIC_LIGHT}, @@ -204,16 +204,16 @@ static uint8_t rgblight_effect_map[][2] = { uint8_t rgblight2xap(uint8_t val) { for(uint8_t i = 0; i < ARRAY_SIZE(rgblight_effect_map); i++) { - if (rgblight_effect_map[i][1] == val) - return rgblight_effect_map[i][0]; + if (pgm_read_byte(&rgblight_effect_map[i][1]) == val) + return pgm_read_byte(&rgblight_effect_map[i][0]); } return 0xFF; } uint8_t xap2rgblight(uint8_t val) { for(uint8_t i = 0; i < ARRAY_SIZE(rgblight_effect_map); i++) { - if (rgblight_effect_map[i][0] == val) - return rgblight_effect_map[i][1]; + if (pgm_read_byte(&rgblight_effect_map[i][0]) == val) + return pgm_read_byte(&rgblight_effect_map[i][1]); } return 0xFF; } @@ -389,7 +389,7 @@ enum { ENABLED_RGBLIGHT_EFFECTS = 0 }; #endif //RGBLIGHT_ENABLE #ifdef RGB_MATRIX_ENABLE -static uint8_t rgb_matrix_effect_map[][2] = { +static const uint8_t rgb_matrix_effect_map[][2] PROGMEM = { #ifdef ENABLE_RGB_MATRIX_SOLID_COLOR { 0x00, RGB_MATRIX_SOLID_COLOR}, @@ -612,16 +612,16 @@ static uint8_t rgb_matrix_effect_map[][2] = { uint8_t rgb_matrix2xap(uint8_t val) { for(uint8_t i = 0; i < ARRAY_SIZE(rgb_matrix_effect_map); i++) { - if (rgb_matrix_effect_map[i][1] == val) - return rgb_matrix_effect_map[i][0]; + if (pgm_read_byte(&rgb_matrix_effect_map[i][1]) == val) + return pgm_read_byte(&rgb_matrix_effect_map[i][0]); } return 0xFF; } uint8_t xap2rgb_matrix(uint8_t val) { for(uint8_t i = 0; i < ARRAY_SIZE(rgb_matrix_effect_map); i++) { - if (rgb_matrix_effect_map[i][0] == val) - return rgb_matrix_effect_map[i][1]; + if (pgm_read_byte(&rgb_matrix_effect_map[i][0]) == val) + return pgm_read_byte(&rgb_matrix_effect_map[i][1]); } return 0xFF; } @@ -847,7 +847,7 @@ enum { ENABLED_RGB_MATRIX_EFFECTS = 0 }; #endif //RGB_MATRIX_ENABLE #ifdef LED_MATRIX_ENABLE -static uint8_t led_matrix_effect_map[][2] = { +static const uint8_t led_matrix_effect_map[][2] PROGMEM = { #ifdef ENABLE_LED_MATRIX_SOLID { 0x00, LED_MATRIX_SOLID}, @@ -963,16 +963,16 @@ static uint8_t led_matrix_effect_map[][2] = { uint8_t led_matrix2xap(uint8_t val) { for(uint8_t i = 0; i < ARRAY_SIZE(led_matrix_effect_map); i++) { - if (led_matrix_effect_map[i][1] == val) - return led_matrix_effect_map[i][0]; + if (pgm_read_byte(&led_matrix_effect_map[i][1]) == val) + return pgm_read_byte(&led_matrix_effect_map[i][0]); } return 0xFF; } uint8_t xap2led_matrix(uint8_t val) { for(uint8_t i = 0; i < ARRAY_SIZE(led_matrix_effect_map); i++) { - if (led_matrix_effect_map[i][0] == val) - return led_matrix_effect_map[i][1]; + if (pgm_read_byte(&led_matrix_effect_map[i][0]) == val) + return pgm_read_byte(&led_matrix_effect_map[i][1]); } return 0xFF; } @@ -1090,3 +1090,4 @@ enum { ENABLED_LED_MATRIX_EFFECTS = 0 #endif }; #endif //LED_MATRIX_ENABLE + From 7fe32bc769b1a5e38bd9ae98979efd9a4e35473a Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Tue, 28 Feb 2023 11:29:58 +1100 Subject: [PATCH 177/203] Start 2023q2 dev cycle. --- readme.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/readme.md b/readme.md index 5649ddfa097a..c2fcda103ece 100644 --- a/readme.md +++ b/readme.md @@ -1,3 +1,7 @@ +# THIS IS THE DEVELOP BRANCH + +Warning- This is the `develop` branch of QMK Firmware. You may encounter broken code here. Please see [Breaking Changes](https://docs.qmk.fm/#/breaking_changes) for more information. + # Quantum Mechanical Keyboard Firmware [![Current Version](https://img.shields.io/github/tag/qmk/qmk_firmware.svg)](https://github.com/qmk/qmk_firmware/tags) From 751e72c1f9320a258fd25547e8cbe8ada25586ff Mon Sep 17 00:00:00 2001 From: zvecr Date: Mon, 6 Mar 2023 19:56:18 +0000 Subject: [PATCH 178/203] Fix xap_client gen after render_xap_output changes --- .../xap/client/python/constants.py.j2 | 10 +++++--- data/templates/xap/client/python/routes.py.j2 | 22 ++++++++-------- data/templates/xap/client/python/types.py.j2 | 25 +++++++++++-------- lib/python/xap_client/constants.py | 4 ++- lib/python/xap_client/routes.py | 4 ++- lib/python/xap_client/types.py | 4 ++- 6 files changed, 41 insertions(+), 28 deletions(-) diff --git a/data/templates/xap/client/python/constants.py.j2 b/data/templates/xap/client/python/constants.py.j2 index a972890dae6c..dcd653765d00 100644 --- a/data/templates/xap/client/python/constants.py.j2 +++ b/data/templates/xap/client/python/constants.py.j2 @@ -5,13 +5,15 @@ from enum import IntEnum # version: 0.0.1 class RgblightModes(IntEnum): -{%- for id, effect in specs.rgblight.effects | dictsort %} +{% for id, effect in specs.rgblight.effects | dictsort %} {{ effect.key }} = {{ id }} -{%- endfor %} +{% endfor %} # version: 0.0.1 class RgbMatrixModes(IntEnum): -{%- for id, effect in specs.rgb_matrix.effects | dictsort %} +{% for id, effect in specs.rgb_matrix.effects | dictsort %} {{ effect.key }} = {{ id }} -{%- endfor %} +{% endfor %} + +# noqa: W391 \ No newline at end of file diff --git a/data/templates/xap/client/python/routes.py.j2 b/data/templates/xap/client/python/routes.py.j2 index 475ee23137dd..51ce0d790077 100644 --- a/data/templates/xap/client/python/routes.py.j2 +++ b/data/templates/xap/client/python/routes.py.j2 @@ -5,16 +5,18 @@ class XAPRouteError(Exception): class XAPRoutes(): -{%- for id, route in xap.routes | dictsort %} -{%- if route.routes %} +{% for id, route in xap.routes | dictsort %} +{% if route.routes %} # {{route.define}} -{%- for subid, subroute in route.routes | dictsort %} +{% for subid, subroute in route.routes | dictsort %} {{route.define}}_{{subroute.define}} = b'\x{{ '%02d' % id|int(base=16) }}\x{{ '%02d' % subid|int(base=16) }}' -{%- if subroute.routes %} -{%- for subsubid, subsubroute in subroute.routes | dictsort %} +{% if subroute.routes %} +{% for subsubid, subsubroute in subroute.routes | dictsort %} {{route.define}}_{{subroute.define}}_{{subsubroute.define}} = b'\x{{ '%02d' % id|int(base=16) }}\x{{ '%02d' % subid|int(base=16) }}\x{{ '%02d' % subsubid|int(base=16) }}' -{%- endfor %} -{%- endif %} -{%- endfor %} -{%- endif %} -{%- endfor %} \ No newline at end of file +{% endfor %} +{% endif %} +{% endfor %} +{% endif %} +{% endfor %} + +# noqa: W391 \ No newline at end of file diff --git a/data/templates/xap/client/python/types.py.j2 b/data/templates/xap/client/python/types.py.j2 index 0c49de3e133f..76f9614e4e3e 100644 --- a/data/templates/xap/client/python/types.py.j2 +++ b/data/templates/xap/client/python/types.py.j2 @@ -4,6 +4,7 @@ from collections import namedtuple from enum import IntFlag, IntEnum from struct import Struct + {% macro gen_struct(name, members, fmt) -%} class {{ name }}(namedtuple('{{ name }}', '{{ members }}')): fmt = Struct('{{ fmt }}') @@ -31,16 +32,16 @@ class {{ name }}(namedtuple('{{ name }}', '{{ members }}')): {% for item in type_definitions -%}{{ gen_struct(item.name, item.members, item.fmt) }}{% endfor -%} # Spec structs -{%- for item in xap.routes.values() recursive -%} +{% for item in xap.routes.values() recursive %} {%- if item.routes -%} {{ loop(item.routes.values()) }} {%- endif -%} - {% if item.request_struct_members %} + {%- if item.request_struct_members %} # TODO: gen inbound object for {{ item.define | to_snake }} - {%- endif -%} - {% if item.return_struct_members %} + {% endif -%} + {%- if item.return_struct_members %} # TODO: gen outbound object for {{ item.define | to_snake }} - {%- endif -%} + {% endif %} {%- endfor %} @@ -51,14 +52,16 @@ class XAPSecureStatus(IntEnum): class XAPEventType(IntEnum): -{%- for id, message in xap.broadcast_messages.messages | dictsort %} +{% for id, message in xap.broadcast_messages.messages | dictsort %} {{ message.define }} = {{ id }} -{%- endfor %} +{% endfor %} class XAPFlags(IntFlag): -{%- for bitnum, bitinfo in xap.response_flags.bits | dictsort %} -{%- if bitinfo.define != "-" %} +{% for bitnum, bitinfo in xap.response_flags.bits | dictsort %} +{% if bitinfo.define != "-" %} {{ bitinfo.define }} = 1 << {{ bitnum }} -{%- endif %} -{%- endfor %} +{% endif %} +{% endfor %} + +# noqa: W391 diff --git a/lib/python/xap_client/constants.py b/lib/python/xap_client/constants.py index f78b13569649..cfde7e153b31 100644 --- a/lib/python/xap_client/constants.py +++ b/lib/python/xap_client/constants.py @@ -1,4 +1,4 @@ -# Copyright 2022 QMK +# Copyright 2023 QMK # SPDX-License-Identifier: GPL-2.0-or-later ################################################################################ @@ -121,3 +121,5 @@ class RgbMatrixModes(IntEnum): MULTISPLASH = 0x29 SOLID_SPLASH = 0x2A SOLID_MULTISPLASH = 0x2B + +# noqa: W391 diff --git a/lib/python/xap_client/routes.py b/lib/python/xap_client/routes.py index 12f3c426f2d3..b30138b48df5 100644 --- a/lib/python/xap_client/routes.py +++ b/lib/python/xap_client/routes.py @@ -1,4 +1,4 @@ -# Copyright 2022 QMK +# Copyright 2023 QMK # SPDX-License-Identifier: GPL-2.0-or-later ################################################################################ @@ -84,3 +84,5 @@ class XAPRoutes(): AUDIO_GET_CONFIG = b'\x07\x03' AUDIO_SET_CONFIG = b'\x07\x04' AUDIO_SAVE_CONFIG = b'\x07\x05' + +# noqa: W391 diff --git a/lib/python/xap_client/types.py b/lib/python/xap_client/types.py index bfdf48961fef..ce75cebab9f1 100644 --- a/lib/python/xap_client/types.py +++ b/lib/python/xap_client/types.py @@ -1,4 +1,4 @@ -# Copyright 2022 QMK +# Copyright 2023 QMK # SPDX-License-Identifier: GPL-2.0-or-later ################################################################################ @@ -133,3 +133,5 @@ class XAPEventType(IntEnum): class XAPFlags(IntFlag): SUCCESS = 1 << 0 SECURE_FAILURE = 1 << 1 + +# noqa: W391 From 2731667ff4faa4408dc256a58a0a745e1b1edcce Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 23 Feb 2023 23:02:38 +0000 Subject: [PATCH 179/203] Custom keycodes in JSON --- data/schemas/definitions.jsonschema | 32 ++++++++++++++ data/schemas/keyboard.jsonschema | 1 + data/schemas/keycodes.jsonschema | 10 ++--- data/schemas/keymap.jsonschema | 1 + keyboards/annepro2/annepro2.c | 3 +- keyboards/annepro2/annepro2.h | 20 --------- keyboards/annepro2/info.json | 19 ++++++++ .../basic/keymaps/dd_keycodes/keymap.json | 12 +++++ lib/python/qmk/cli/generate/api.py | 6 +++ lib/python/qmk/cli/generate/keyboard_h.py | 44 ++++++++++++++++--- lib/python/qmk/info.py | 6 +++ lib/python/qmk/keymap.py | 30 +++++++++++++ 12 files changed, 149 insertions(+), 35 deletions(-) create mode 100644 keyboards/handwired/pytest/basic/keymaps/dd_keycodes/keymap.json diff --git a/data/schemas/definitions.jsonschema b/data/schemas/definitions.jsonschema index 94a94157c0c5..b9c64a55ec44 100644 --- a/data/schemas/definitions.jsonschema +++ b/data/schemas/definitions.jsonschema @@ -71,6 +71,38 @@ "type": "string", "pattern": "^[0-9a-z][0-9a-z_/]*$" }, + "keycode": { + "type": "string", + "minLength": 2, + "maxLength": 50, + "pattern": "^[A-Z][A-Zs_0-9]*$" + }, + "keycode_short": { + "type": "string", + "minLength": 2, + "maxLength": 7, + "pattern": "^[A-Z][A-Zs_0-9]*$" + }, + "keycode_decl": { + "type": "object", + "required": [ + "key" + ], + "properties": { + "key": {"$ref": "#/keycode"}, + "label": {"$ref": "#/text_identifier"}, + "aliases": { + "type": "array", + "minItems": 1, + "items": {"$ref": "#/keycode_short"} + } + } + }, + "keycode_decl_array": { + "type": "array", + "minItems": 1 + "items": {"$ref": "#/keycode_decl"} + }, "mcu_pin_array": { "type": "array", "items": {"$ref": "#/mcu_pin"} diff --git a/data/schemas/keyboard.jsonschema b/data/schemas/keyboard.jsonschema index 2afc1ed516f1..edabeb856c61 100644 --- a/data/schemas/keyboard.jsonschema +++ b/data/schemas/keyboard.jsonschema @@ -246,6 +246,7 @@ "on_state": {"$ref": "qmk.definitions.v1#/bit"} } }, + "keycodes": {"$ref": "qmk.definitions.v1#/keycode_decl_array"}, "layout_aliases": { "type": "object", "additionalProperties": {"$ref": "qmk.definitions.v1#/layout_macro"} diff --git a/data/schemas/keycodes.jsonschema b/data/schemas/keycodes.jsonschema index 77a8347b3b78..df6ce95a83be 100644 --- a/data/schemas/keycodes.jsonschema +++ b/data/schemas/keycodes.jsonschema @@ -8,11 +8,7 @@ "type": "string", "minLength": 2, "maxLength": 50, - "pattern": "^[A-Zs_0-9]*$" - }, - "hex_number_4d": { - "type": "string", - "pattern": "^0x[0-9A-F]{4}$" + "pattern": "^[A-Z][A-Zs_0-9]*$" } }, "properties": { @@ -34,10 +30,10 @@ "keycodes": { "type": "object", "propertyNames": { - "$ref": "#/definitions/hex_number_4d" + "$ref": "qmk.definitions.v1#/hex_number_4d" }, "additionalProperties": { - "type": "object", + "type": "object", // use 'qmk.definitions.v1#/keycode_decl' when problem keycodes are removed "required": [ "key" ], diff --git a/data/schemas/keymap.jsonschema b/data/schemas/keymap.jsonschema index 73aa7c5c2226..7233e896e940 100644 --- a/data/schemas/keymap.jsonschema +++ b/data/schemas/keymap.jsonschema @@ -67,6 +67,7 @@ } } }, + "keycodes": {"$ref": "qmk.definitions.v1#/keycode_decl_array"}, "config": {"$ref": "qmk.keyboard.v1"}, "notes": { "type": "string" diff --git a/keyboards/annepro2/annepro2.c b/keyboards/annepro2/annepro2.c index e1292b351720..93678f098bc2 100644 --- a/keyboards/annepro2/annepro2.c +++ b/keyboards/annepro2/annepro2.c @@ -13,9 +13,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - +#include QMK_KEYBOARD_H #include "hal.h" -#include "annepro2.h" #include "annepro2_ble.h" #include "spi_master.h" #include "ap2_led.h" diff --git a/keyboards/annepro2/annepro2.h b/keyboards/annepro2/annepro2.h index ad1fce6b0311..cebe3d0c86a7 100644 --- a/keyboards/annepro2/annepro2.h +++ b/keyboards/annepro2/annepro2.h @@ -57,23 +57,3 @@ extern ble_capslock_t ble_capslock; /* ROW5 */ { K40, KC_NO, K42, K43, KC_NO, KC_NO, K46, KC_NO, KC_NO, K49, K4A, K4B, K4C, KC_NO}, \ } // clang-format on - -enum AP2KeyCodes { - KC_AP2_BT1 = QK_KB_0, - KC_AP2_BT2, - KC_AP2_BT3, - KC_AP2_BT4, - KC_AP2_BT_UNPAIR, - KC_AP2_USB, - KC_AP_LED_ON, - KC_AP_LED_OFF, - KC_AP_LED_TOG, - KC_AP_LED_NEXT_PROFILE, - KC_AP_LED_PREV_PROFILE, - KC_AP_LED_NEXT_INTENSITY, - KC_AP_LED_SPEED, - KC_AP_RGB_VAI, - KC_AP_RGB_VAD, - KC_AP_RGB_TOG, - KC_AP_RGB_MOD, -}; diff --git a/keyboards/annepro2/info.json b/keyboards/annepro2/info.json index dbeaf6faed8e..8dc8c3b02c6d 100644 --- a/keyboards/annepro2/info.json +++ b/keyboards/annepro2/info.json @@ -6,6 +6,25 @@ "vid": "0xFEED", "device_version": "13.3.7" }, + "keycodes": [ + { "key": "KC_AP2_BT1", "aliases": ["AP2_BT1"] }, + { "key": "KC_AP2_BT2", "aliases": ["AP2_BT2"] }, + { "key": "KC_AP2_BT3", "aliases": ["AP2_BT3"] }, + { "key": "KC_AP2_BT4", "aliases": ["AP2_BT4"] }, + { "key": "KC_AP2_BT_UNPAIR" }, + { "key": "KC_AP2_USB" }, + { "key": "KC_AP_LED_ON" }, + { "key": "KC_AP_LED_OFF" }, + { "key": "KC_AP_LED_TOG" }, + { "key": "KC_AP_LED_NEXT_PROFILE" }, + { "key": "KC_AP_LED_PREV_PROFILE" }, + { "key": "KC_AP_LED_NEXT_INTENSITY" }, + { "key": "KC_AP_LED_SPEED" }, + { "key": "KC_AP_RGB_VAI" }, + { "key": "KC_AP_RGB_VAD" }, + { "key": "KC_AP_RGB_TOG" }, + { "key": "KC_AP_RGB_MOD" } + ], "layouts": { "LAYOUT_60_ansi": { "layout": [ diff --git a/keyboards/handwired/pytest/basic/keymaps/dd_keycodes/keymap.json b/keyboards/handwired/pytest/basic/keymaps/dd_keycodes/keymap.json new file mode 100644 index 000000000000..a64a65a80aaf --- /dev/null +++ b/keyboards/handwired/pytest/basic/keymaps/dd_keycodes/keymap.json @@ -0,0 +1,12 @@ +{ + "keyboard": "handwired/pytest/basic", + "keymap": "default_json", + "layout": "LAYOUT_ortho_1x1", + "layers": [["EXAMPLE_1"]], + "keycodes": [ + { "key": "EXAMPLE_1" } + ], + "author": "qmk", + "notes": "This file is a keymap.json file for handwired/pytest/basic", + "version": 1 +} diff --git a/lib/python/qmk/cli/generate/api.py b/lib/python/qmk/cli/generate/api.py index 11d4616199d3..cfea3f3946f5 100755 --- a/lib/python/qmk/cli/generate/api.py +++ b/lib/python/qmk/cli/generate/api.py @@ -67,6 +67,12 @@ def _filtered_copy(src, dst): dst.write_text(json.dumps(data), encoding='utf-8') return dst + if dst.suffix == '.jsonschema': + data = json_load(src) + + dst.write_text(json.dumps(data), encoding='utf-8') + return dst + return shutil.copy2(src, dst) diff --git a/lib/python/qmk/cli/generate/keyboard_h.py b/lib/python/qmk/cli/generate/keyboard_h.py index 152921bdce60..fa4036e39a68 100755 --- a/lib/python/qmk/cli/generate/keyboard_h.py +++ b/lib/python/qmk/cli/generate/keyboard_h.py @@ -11,12 +11,9 @@ from qmk.constants import COL_LETTERS, ROW_LETTERS, GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE -def _generate_layouts(keyboard): - """Generates the layouts.h file. +def _generate_layouts(keyboard, kb_info_json): + """Generates the layouts macros. """ - # Build the info.json file - kb_info_json = info_json(keyboard) - if 'matrix_size' not in kb_info_json: cli.log.error(f'{keyboard}: Invalid matrix config.') return [] @@ -65,6 +62,32 @@ def _generate_layouts(keyboard): return lines +def _generate_keycodes(kb_info_json): + """Generates keyboard level keycodes. + """ + if 'keycodes' not in kb_info_json: + return [] + + lines = [] + lines.append('enum keyboard_keycodes {') + + for index, item in enumerate(kb_info_json.get('keycodes')): + key = item["key"] + if index == 0: + lines.append(f' {key} = QK_KB_0,') + else: + lines.append(f' {key},') + + lines.append('};') + + for item in kb_info_json.get('keycodes', []): + key = item["key"] + for alias in item.get("aliases", []): + lines.append(f'#define {alias} {key}') + + return lines + + @cli.argument('-i', '--include', nargs='?', arg_only=True, help='Optional file to include') @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") @@ -73,8 +96,12 @@ def _generate_layouts(keyboard): def generate_keyboard_h(cli): """Generates the keyboard.h file. """ + # Build the info.json file + kb_info_json = info_json(cli.args.keyboard) + keyboard_h = cli.args.include - dd_layouts = _generate_layouts(cli.args.keyboard) + dd_layouts = _generate_layouts(cli.args.keyboard, kb_info_json) + dd_keycodes = _generate_keycodes(kb_info_json) valid_config = dd_layouts or keyboard_h # Build the layouts.h file. @@ -87,6 +114,11 @@ def generate_keyboard_h(cli): if keyboard_h: keyboard_h_lines.append(f'#include "{Path(keyboard_h).name}"') + keyboard_h_lines.append('') + keyboard_h_lines.append('// Keycode content') + if dd_keycodes: + keyboard_h_lines.extend(dd_keycodes) + # Protect against poorly configured keyboards if not valid_config: keyboard_h_lines.append('#error(".h is required unless your keyboard uses data-driven configuration. Please rename your keyboard\'s header file to .h")') diff --git a/lib/python/qmk/info.py b/lib/python/qmk/info.py index 152e6ce7b6d0..202a922a39ff 100644 --- a/lib/python/qmk/info.py +++ b/lib/python/qmk/info.py @@ -93,6 +93,12 @@ def _validate(keyboard, info_data): if layout_name not in layouts and layout_name not in layout_aliases: _log_error(info_data, 'Claims to support community layout %s but no %s() macro found' % (layout, layout_name)) + # keycodes with length > 7 must have short forms for visualisation purposes + for decl in info_data.get('keycodes', []): + if len(decl["key"]) > 7: + if not decl.get("aliases", []): + _log_error(info_data, f'Keycode {decl["key"]} has no short form alias') + def info_json(keyboard): """Generate the info.json data for a specific keyboard. diff --git a/lib/python/qmk/keymap.py b/lib/python/qmk/keymap.py index dddf6449a7bc..8ae8a5ed1999 100644 --- a/lib/python/qmk/keymap.py +++ b/lib/python/qmk/keymap.py @@ -25,6 +25,7 @@ * This file was generated by qmk json2c. You may or may not want to * edit it directly. */ +__KEYCODE_OUTPUT_GOES_HERE__ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { __KEYMAP_GOES_HERE__ @@ -123,6 +124,29 @@ def _generate_macros_function(keymap_json): return macro_txt +def _generate_keycodes_function(keymap_json): + """Generates keymap level keycodes. + """ + lines = [] + lines.append('enum keymap_keycodes {') + + for index, item in enumerate(keymap_json.get('keycodes', [])): + key = item["key"] + if index == 0: + lines.append(f' {key} = QK_USER_0,') + else: + lines.append(f' {key},') + + lines.append('};') + + for item in keymap_json.get('keycodes', []): + key = item["key"] + for alias in item.get("aliases", []): + lines.append(f'#define {alias} {key}') + + return lines + + def template_json(keyboard): """Returns a `keymap.json` template for a keyboard. @@ -317,6 +341,12 @@ def generate_c(keymap_json): hostlang = f'#include "keymap_{keymap_json["host_language"]}.h"\n#include "sendstring_{keymap_json["host_language"]}.h"\n' new_keymap = new_keymap.replace('__INCLUDES__', hostlang) + keycodes = '' + if 'keycodes' in keymap_json and keymap_json['keycodes'] is not None: + keycodes_txt = _generate_keycodes_function(keymap_json) + keycodes = '\n'.join(keycodes_txt) + new_keymap = new_keymap.replace('__KEYCODE_OUTPUT_GOES_HERE__', keycodes) + return new_keymap From 64c5531be666e6ba288ff3495654c9f736937dd9 Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 22 Mar 2023 19:27:51 +0000 Subject: [PATCH 180/203] Refactor inline_generator to template --- .../xap/firmware/xap_generated.inl.j2 | 129 +++++++- lib/python/qmk/cli/xap/generate_qmk.py | 5 +- lib/python/qmk/xap/common.py | 14 - .../qmk/xap/gen_firmware/inline_generator.py | 298 ------------------ 4 files changed, 120 insertions(+), 326 deletions(-) delete mode 100755 lib/python/qmk/xap/gen_firmware/inline_generator.py diff --git a/data/templates/xap/firmware/xap_generated.inl.j2 b/data/templates/xap/firmware/xap_generated.inl.j2 index 79359fb315ca..b682a25306f6 100755 --- a/data/templates/xap/firmware/xap_generated.inl.j2 +++ b/data/templates/xap/firmware/xap_generated.inl.j2 @@ -1,8 +1,25 @@ {{ constants.GPL2_HEADER_C_LIKE }} {{ constants.GENERATED_HEADER_C_LIKE }} +{% macro route_conditions(route_stack) %} +{% set conditions = [] %} +{% for data in route_stack %} +{% if 'enable_if_preprocessor' in data %} +{{ conditions.append(data.enable_if_preprocessor) or '' }} +{% endif %} +{% endfor %} + +{% if conditions %} +#if ({{ conditions | join(' && ') }}) +{% endif %} +{{ caller() }} +{%- if conditions %} +#endif // ({{ conditions | join(' && ') }}) +{% endif %} +{% endmacro %} + //////////////////////////////////////////////////////////////////////////////// -// Full XAP {{ xap.version }} definitions +// Broadcast {% for message_id,data in xap.broadcast_messages.messages | dictsort %} {% if 'return_type' in data %} @@ -12,24 +29,116 @@ void {{ xap.broadcast_messages.define_prefix | lower }}_{{ data.define | to_snak {% endif %} {% endfor %} +//////////////////////////////////////////////////////////////////////////////// +// Decl + +{% macro export_route_declaration(container) %} +{% if 'routes' in container %} +{% for route, data in container.routes | dictsort %} +{% if 'return_execute' in data %} +bool xap_respond_{{ data.return_execute }}(xap_token_t token, const uint8_t *data, size_t data_len); +{% endif %} +{{ export_route_declaration(data) }} +{% endfor %} +{% endif %} +{% endmacro %} + +{{ export_route_declaration(xap) }} + +//////////////////////////////////////////////////////////////////////////////// +// Data + +{% macro export_route_data(prefix, container, route_stack) %} +{% set this_route_stack = route_stack.copy() %} +{{ this_route_stack.append(container) or '' }} +{% if 'routes' in container %} + {% for route, data in container.routes | dictsort %} + {% set this_prefix_uc = (prefix + '_' + data.define) | upper %} + {% set this_prefix_lc = this_prefix_uc | lower %} + {% if 'return_constant' in data %} + {% if data.return_type == 'struct' %} +{% call route_conditions(this_route_stack) %} +static const {{ this_prefix_lc }}_t {{ this_prefix_lc }}_data PROGMEM = { +{% for member in data.return_constant %} + {{ member }}, +{% endfor %} +}; +{% endcall %} + {% elif data.return_type == 'string' %} +{% call route_conditions(this_route_stack) %} +static const char {{ this_prefix_lc }}_str[] PROGMEM = {{ data.return_constant }}; +{% endcall %} + {% else %} +{% call route_conditions(this_route_stack) %} +static const {{ data.return_type | type_to_c_before }} {{ this_prefix_lc }}_data PROGMEM = {{ data.return_constant }}; +{% endcall %} + {% endif %} + {% endif %} +{{ export_route_data(this_prefix_lc, data, this_route_stack) }} + {% endfor %} +{% endif %} +{% endmacro %} + +{{ export_route_data('XAP_ROUTE', xap, []) }} + +//////////////////////////////////////////////////////////////////////////////// +// Routes + {% macro append_routing_table(prefix, container, route_stack) %} {% set this_route_stack = route_stack.copy() %} -{{ this_route_stack.append(container) }} -{% set stack_names = this_route_stack | map(attribute='name') | join(', ') %} -Stack names: {{ stack_names }} +{{ this_route_stack.append(container) or '' }} {% if 'routes' in container %} {% for route, data in container.routes | dictsort %} {% set this_prefix_uc = (prefix + '_' + data.define) | upper %} {% set this_prefix_lc = this_prefix_uc | lower %} {{ append_routing_table(this_prefix_lc, data, this_route_stack) }} -Inner route prefix for {{ prefix }}: {{ this_prefix_lc }} {% endfor %} +{% call route_conditions(this_route_stack) %} +static const xap_route_t {{ prefix | lower}}_table[] PROGMEM = { +{% for route, data in container.routes | dictsort %} +{% set inner_route_stack = this_route_stack.copy() %} +{{ inner_route_stack.append(data) or '' }} +{% if 'permissions' in data %} +{% set secure_status = 'ROUTE_PERMISSIONS_SECURE' %} +{% else %} +{% set secure_status = 'ROUTE_PERMISSIONS_INSECURE' %} +{% endif %} +{% call route_conditions(inner_route_stack) %} + [{{ prefix | upper }}_{{ data.define }}] = { +{% if 'routes' in data %} + .flags = { + .type = XAP_ROUTE, + .secure = {{ secure_status }}, + }, + .child_routes = {{ prefix | lower }}_{{ data.define | lower }}_table, + .child_routes_len = sizeof({{ prefix | lower }}_{{ data.define | lower }}_table)/sizeof(xap_route_t), +{% elif 'return_execute' in data %} + .flags = { + .type = XAP_EXECUTE, + .secure = {{ secure_status }}, + }, + .handler = xap_respond_{{ data.return_execute | lower }}, +{% elif 'return_constant' in data and data.return_type == 'string' %} + .flags = { + .type = XAP_CONST_MEM, + .secure = {{ secure_status }}, + }, + .const_data = {{ prefix | lower }}_{{ data.define | lower }}_str, + .const_data_len = sizeof({{ prefix | lower }}_{{ data.define | lower }}_str) - 1, +{% elif 'return_constant' in data %} + .flags = { + .type = XAP_CONST_MEM, + .secure = {{ secure_status }}, + }, + .const_data = &{{ prefix | lower }}_{{ data.define | lower }}_data, + .const_data_len = sizeof({{ prefix | lower }}_{{ data.define | lower }}_data), +{% endif %} + }, +{% endcall %} +{% endfor %} +}; +{% endcall %} {% endif %} -Prefix: {{ prefix }} {% endmacro %} {{ append_routing_table("xap_route", xap, []) }} - -#if 0 -{{ xap | tojson(indent=4) }} -#endif \ No newline at end of file diff --git a/lib/python/qmk/cli/xap/generate_qmk.py b/lib/python/qmk/cli/xap/generate_qmk.py index 06ec8a0bf1be..67ec93227477 100755 --- a/lib/python/qmk/cli/xap/generate_qmk.py +++ b/lib/python/qmk/cli/xap/generate_qmk.py @@ -6,7 +6,6 @@ from qmk.keyboard import keyboard_completer, keyboard_folder from qmk.xap.common import render_xap_output, merge_xap_defs from qmk.xap.gen_firmware.blob_generator import generate_blob -from qmk.xap.gen_firmware.inline_generator import generate_inline @cli.argument('-o', '--output', type=normpath, help='File to write to') @@ -26,10 +25,8 @@ def xap_generate_qmk_inc(cli): cli.subcommands['xap-generate-qmk-inc'].print_help() return False - generate_inline(cli.args.output, cli.args.keyboard, cli.args.keymap) - defs = merge_xap_defs(cli.args.keyboard, cli.args.keymap) - with open(normpath(str(cli.args.output.resolve()) + '.generated.j2.c'), 'w', encoding='utf-8') as out_file: + with open(cli.args.output, 'w', encoding='utf-8') as out_file: r = render_xap_output('firmware', 'xap_generated.inl.j2', defs, keyboard=cli.args.keyboard, keymap=cli.args.keymap) while r.find('\n\n\n') != -1: r = r.replace('\n\n\n', '\n\n') diff --git a/lib/python/qmk/xap/common.py b/lib/python/qmk/xap/common.py index 9f84ab8a02fa..b3d0b2aaaf4b 100755 --- a/lib/python/qmk/xap/common.py +++ b/lib/python/qmk/xap/common.py @@ -172,17 +172,3 @@ def merge_xap_defs(kb, km): exit(1) return defs - - -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]) + ")" diff --git a/lib/python/qmk/xap/gen_firmware/inline_generator.py b/lib/python/qmk/xap/gen_firmware/inline_generator.py deleted file mode 100755 index 1dd72233a843..000000000000 --- a/lib/python/qmk/xap/gen_firmware/inline_generator.py +++ /dev/null @@ -1,298 +0,0 @@ -"""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'] == 'u64': - 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])) - - condition = route_conditions(route_stack) - if condition: - lines.append(f'#if {condition}') - - 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'] == 'u64': - constant = container['return_constant'] - lines.append('') - lines.append(f'static const uint64_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 - - if condition: - lines.append(f'#endif // {condition}') - lines.append('') - - 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', - } - - 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'] == 'u64': - _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) From 5f7e520f8ff3f10be9696d68ab610e8f10255b1d Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 22 Mar 2023 21:19:42 +0000 Subject: [PATCH 181/203] Strip out XAP_GETTER for now --- quantum/xap/xap.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/quantum/xap/xap.c b/quantum/xap/xap.c index 26f896b75ef4..f3ac73d90233 100644 --- a/quantum/xap/xap.c +++ b/quantum/xap/xap.c @@ -48,7 +48,6 @@ typedef enum xap_route_type_t { XAP_ROUTE, XAP_EXECUTE, XAP_VALUE, - XAP_GETTER, XAP_CONST_MEM, TOTAL_XAP_ROUTE_TYPES } xap_route_type_t; @@ -83,9 +82,6 @@ struct __attribute__((packed)) xap_route_t { // 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; @@ -136,14 +132,6 @@ void xap_execute_route(xap_token_t token, const xap_route_t *routes, size_t max_ } 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; From 7edc0bcd43b171c3dd1faf144dfe914258c9533a Mon Sep 17 00:00:00 2001 From: zvecr Date: Wed, 22 Mar 2023 21:20:55 +0000 Subject: [PATCH 182/203] Tidy up bool implementation --- data/xap/xap_0.1.0.hjson | 4 ++++ lib/python/qmk/xap/jinja2_filters.py | 2 ++ 2 files changed, 6 insertions(+) diff --git a/data/xap/xap_0.1.0.hjson b/data/xap/xap_0.1.0.hjson index 366f2cbf3185..749b9ccc4142 100755 --- a/data/xap/xap_0.1.0.hjson +++ b/data/xap/xap_0.1.0.hjson @@ -38,6 +38,10 @@ } type_docs: { + bool: + ''' + Data type that contains values 0 and 1. Implementaed as an alias of `u8`. + ''' u64: ''' An unsigned 64-bit integral, commonly seen as `uint64_t` from _stdint.h_. diff --git a/lib/python/qmk/xap/jinja2_filters.py b/lib/python/qmk/xap/jinja2_filters.py index c5b2aba55fd1..043d20f8c3ac 100644 --- a/lib/python/qmk/xap/jinja2_filters.py +++ b/lib/python/qmk/xap/jinja2_filters.py @@ -27,6 +27,8 @@ def _xap_type_to_c_before(xt: str): return 'uint32_t' elif xt == 'u64': return 'uint64_t' + elif xt == 'bool': + return 'uint8_t' elif xt == 'string': return 'const char*' elif xt == 'token': From 5eed80c66b637be227f5260c37d8a70a77c18b46 Mon Sep 17 00:00:00 2001 From: zvecr Date: Thu, 23 Mar 2023 00:46:25 +0000 Subject: [PATCH 183/203] Fix up after merge --- quantum/xap/handlers/audio.c | 2 +- quantum/xap/handlers/lighting.c | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/quantum/xap/handlers/audio.c b/quantum/xap/handlers/audio.c index b70a7f9c4f95..b06ec0311cc4 100644 --- a/quantum/xap/handlers/audio.c +++ b/quantum/xap/handlers/audio.c @@ -32,7 +32,7 @@ bool xap_respond_set_audio_config(xap_token_t token, const void *data, size_t le } bool xap_respond_save_audio_config(xap_token_t token, const void *data, size_t length) { - eeconfig_update_audio(audio_config.raw); + eeconfig_update_audio_current(); return xap_respond_success(token); } diff --git a/quantum/xap/handlers/lighting.c b/quantum/xap/handlers/lighting.c index 56726d19ac8a..fae3dfe211af 100644 --- a/quantum/xap/handlers/lighting.c +++ b/quantum/xap/handlers/lighting.c @@ -60,10 +60,6 @@ extern rgblight_config_t rgblight_config; uint8_t rgblight2xap(uint8_t val); uint8_t xap2rgblight(uint8_t val); -void rgblight_enabled_noeeprom(bool val) { - val ? rgblight_enable_noeeprom() : rgblight_disable_noeeprom(); -} - bool xap_respond_get_rgblight_config(xap_token_t token, const void *data, size_t length) { xap_route_lighting_rgblight_get_config_t ret; From 42895141de2363783e3455bef734cd0d81395c85 Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 21 Mar 2023 15:05:40 +0000 Subject: [PATCH 184/203] Decouple lighting_map from XAP --- .../led_matrix_0.0.1.hjson} | 0 .../rgb_matrix_0.0.1.hjson} | 0 .../rgblight_0.0.1.hjson} | 0 lib/python/qmk/cli/__init__.py | 2 +- .../lighting_map.py} | 78 +- lib/python/qmk/lighting.py | 37 + lib/python/qmk/xap/common.py | 38 +- quantum/led_matrix/lighting_map.h | 263 ++++ quantum/rgb_matrix/lighting_map.h | 477 +++++++ quantum/rgblight/lighting_map.h | 377 ++++++ quantum/xap/handlers/lighting.c | 16 +- quantum/xap/lighting_map.h | 1093 ----------------- util/regen.sh | 3 + 13 files changed, 1206 insertions(+), 1178 deletions(-) rename data/constants/{led_matrix_0.0.1.json => lighting/led_matrix_0.0.1.hjson} (100%) rename data/constants/{rgb_matrix_0.0.1.json => lighting/rgb_matrix_0.0.1.hjson} (100%) rename data/constants/{rgblight_0.0.1.json => lighting/rgblight_0.0.1.hjson} (100%) rename lib/python/qmk/cli/{xap/generate_lighting.py => generate/lighting_map.py} (63%) create mode 100644 lib/python/qmk/lighting.py create mode 100644 quantum/led_matrix/lighting_map.h create mode 100644 quantum/rgb_matrix/lighting_map.h create mode 100644 quantum/rgblight/lighting_map.h delete mode 100644 quantum/xap/lighting_map.h diff --git a/data/constants/led_matrix_0.0.1.json b/data/constants/lighting/led_matrix_0.0.1.hjson similarity index 100% rename from data/constants/led_matrix_0.0.1.json rename to data/constants/lighting/led_matrix_0.0.1.hjson diff --git a/data/constants/rgb_matrix_0.0.1.json b/data/constants/lighting/rgb_matrix_0.0.1.hjson similarity index 100% rename from data/constants/rgb_matrix_0.0.1.json rename to data/constants/lighting/rgb_matrix_0.0.1.hjson diff --git a/data/constants/rgblight_0.0.1.json b/data/constants/lighting/rgblight_0.0.1.hjson similarity index 100% rename from data/constants/rgblight_0.0.1.json rename to data/constants/lighting/rgblight_0.0.1.hjson diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py index b68e769a2a70..b562b629286c 100644 --- a/lib/python/qmk/cli/__init__.py +++ b/lib/python/qmk/cli/__init__.py @@ -58,6 +58,7 @@ 'qmk.cli.generate.keyboard_h', 'qmk.cli.generate.keycodes', 'qmk.cli.generate.keycodes_tests', + 'qmk.cli.generate.lighting_map', 'qmk.cli.generate.rgb_breathe_table', 'qmk.cli.generate.rules_mk', 'qmk.cli.generate.version_h', @@ -83,7 +84,6 @@ 'qmk.cli.xap', 'qmk.cli.xap.generate_docs', 'qmk.cli.xap.generate_json', - 'qmk.cli.xap.generate_lighting', 'qmk.cli.xap.generate_python', 'qmk.cli.xap.generate_qmk', ] diff --git a/lib/python/qmk/cli/xap/generate_lighting.py b/lib/python/qmk/cli/generate/lighting_map.py similarity index 63% rename from lib/python/qmk/cli/xap/generate_lighting.py rename to lib/python/qmk/cli/generate/lighting_map.py index e19639a65fe6..9a44085ba4ac 100644 --- a/lib/python/qmk/cli/xap/generate_lighting.py +++ b/lib/python/qmk/cli/generate/lighting_map.py @@ -2,7 +2,7 @@ from qmk.path import normpath from qmk.commands import dump_lines -from qmk.xap.common import load_lighting_spec +from qmk.lighting import load_lighting_spec from qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE PREFIX_MAP = { @@ -21,8 +21,21 @@ } +def _always_enabled(id): + """Assumption that first effect is always enabled + """ + return id == '0x00' + + +def _wrap_ifdef(line, define): + return f''' +#ifdef {define} +{line} +#endif''' + + def _append_lighting_map(lines, feature, spec): - """TODO: + """Translate effect to 'constant id'->'firmware id' lookup table """ groups = spec.get('groups', {}) ifdef_prefix = PREFIX_MAP[feature]['ifdef'] @@ -33,17 +46,14 @@ def _append_lighting_map(lines, feature, spec): define = obj['define'] offset = f' + {obj["offset"]}' if obj['offset'] else '' - line = f''' -#ifdef {ifdef_prefix}_{define} - {{ {id}, {def_prefix}_{define}{offset}}}, -#endif''' + line = f'{{ {id}, {def_prefix}_{define}{offset}}},' + + if not _always_enabled(id): + line = _wrap_ifdef(line, f'{ifdef_prefix}_{define}') group = groups.get(obj.get('group', None), {}).get('define', None) if group: - line = f''' -#ifdef {group} -{line} -#endif''' + line = _wrap_ifdef(line, group) lines.append(line) @@ -52,7 +62,7 @@ def _append_lighting_map(lines, feature, spec): # add helper funcs lines.append( f''' -uint8_t {feature}2xap(uint8_t val) {{ +uint8_t {feature}_effect_to_id(uint8_t val) {{ for(uint8_t i = 0; i < ARRAY_SIZE({feature}_effect_map); i++) {{ if (pgm_read_byte(&{feature}_effect_map[i][1]) == val) return pgm_read_byte(&{feature}_effect_map[i][0]); @@ -60,7 +70,7 @@ def _append_lighting_map(lines, feature, spec): return 0xFF; }} -uint8_t xap2{feature}(uint8_t val) {{ +uint8_t {feature}_id_to_effect(uint8_t val) {{ for(uint8_t i = 0; i < ARRAY_SIZE({feature}_effect_map); i++) {{ if (pgm_read_byte(&{feature}_effect_map[i][0]) == val) return pgm_read_byte(&{feature}_effect_map[i][1]); @@ -70,8 +80,8 @@ def _append_lighting_map(lines, feature, spec): ) -def _append_lighting_bitmask(lines, feature, spec): - """TODO: +def _append_lighting_bit_field(lines, feature, spec): + """Translate effect to bit of bit-field """ groups = spec.get('groups', {}) ifdef_prefix = PREFIX_MAP[feature]['ifdef'] @@ -80,49 +90,37 @@ def _append_lighting_bitmask(lines, feature, spec): for id, obj in spec.get('effects', {}).items(): define = obj['define'] - line = f''' -#ifdef {ifdef_prefix}_{define} - | (1ULL << {id}) -#endif''' + line = f' | (1ULL << {id})' + + if not _always_enabled(id): + line = _wrap_ifdef(line, f'{ifdef_prefix}_{define}') group = groups.get(obj.get('group', None), {}).get('define', None) if group: - line = f''' -#ifdef {group} -{line} -#endif''' + line = _wrap_ifdef(line, group) lines.append(line) lines.append('};') -def _append_lighting_mapping(lines): - """TODO: +def _append_lighting_mapping(lines, feature): + """Generate lookup table and bit-field of effect """ - # TODO: remove bodge for always enabled effects - lines.append(''' -#define RGBLIGHT_EFFECT_STATIC_LIGHT -#define ENABLE_RGB_MATRIX_SOLID_COLOR -#define ENABLE_LED_MATRIX_SOLID -''') - - for feature in PREFIX_MAP.keys(): - spec = load_lighting_spec(feature) + spec = load_lighting_spec(feature) - lines.append(f'#ifdef {feature.upper()}_ENABLE') - _append_lighting_map(lines, feature, spec) - _append_lighting_bitmask(lines, feature, spec) - lines.append(f'#endif //{feature.upper()}_ENABLE') + _append_lighting_bit_field(lines, feature, spec) + _append_lighting_map(lines, feature, spec) @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('-q', '--quiet', arg_only=True, action='store_true', help='Quiet mode, only output error messages') +@cli.argument('-f', '--feature', required=True, help='Feature to generate map', choices=PREFIX_MAP.keys()) @cli.subcommand('Generates effect header.') -def xap_generate_lighting_map(cli): +def generate_lighting_map(cli): # Preamble lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', '// clang-format off'] - _append_lighting_mapping(lines) + _append_lighting_mapping(lines, cli.args.feature) dump_lines(cli.args.output, lines, cli.args.quiet) diff --git a/lib/python/qmk/lighting.py b/lib/python/qmk/lighting.py new file mode 100644 index 000000000000..2a81cf9e8d79 --- /dev/null +++ b/lib/python/qmk/lighting.py @@ -0,0 +1,37 @@ +import re +from pathlib import Path + +from qmk.json_schema import json_load + + +def list_lighting_versions(feature): + """Return available versions - sorted newest first + """ + ret = [] + for file in Path('data/constants/lighting/').glob(f'{feature}_[0-9].[0-9].[0-9].hjson'): + ret.append(file.stem.split('_')[-1]) + + ret.sort(reverse=True) + return ret + + +def load_lighting_spec(feature, version='latest'): + """Build lighting data from the requested spec file + """ + if version == 'latest': + version = list_lighting_versions(feature)[0] + + spec = json_load(Path(f'data/constants/lighting/{feature}_{version}.hjson')) + + # preprocess for gross rgblight "mode + n" + for obj in spec.get('effects', {}).values(): + define = obj['key'] + offset = 0 + found = re.match('(.*)_(\\d+)$', define) + if found: + define = found.group(1) + offset = int(found.group(2)) - 1 + obj['define'] = define + obj['offset'] = offset + + return spec diff --git a/lib/python/qmk/xap/common.py b/lib/python/qmk/xap/common.py index b3d0b2aaaf4b..827e4162f6ca 100755 --- a/lib/python/qmk/xap/common.py +++ b/lib/python/qmk/xap/common.py @@ -1,7 +1,5 @@ """This script handles the XAP protocol data files. """ -import re -import os import hjson import jsonschema from pathlib import Path @@ -10,6 +8,7 @@ import qmk.constants from qmk.git import git_get_version +from qmk.lighting import load_lighting_spec from qmk.json_schema import json_load, validate, merge_ordered_dicts from qmk.makefile import parse_rules_mk_file from qmk.decorators import lru_cache @@ -21,41 +20,8 @@ XAP_SPEC = 'xap.hjson' -def list_lighting_versions(feature): - """Return available versions - sorted newest first - """ - ret = [] - for file in Path('data/constants/').glob(f'{feature}_[0-9].[0-9].[0-9].json'): - ret.append(file.stem.split('_')[-1]) - - ret.sort(reverse=True) - return ret - - -def load_lighting_spec(feature, version='latest'): - """Build lighting data from the requested spec file - """ - if version == 'latest': - version = list_lighting_versions(feature)[0] - - spec = json_load(Path(f'data/constants/{feature}_{version}.json')) - - # preprocess for gross rgblight "mode + n" - for obj in spec.get('effects', {}).values(): - define = obj['key'] - offset = 0 - found = re.match('(.*)_(\\d+)$', define) - if found: - define = found.group(1) - offset = int(found.group(2)) - 1 - obj['define'] = define - obj['offset'] = offset - - return spec - - def _get_jinja2_env(data_templates_xap_subdir: str): - templates_dir = os.path.join(qmk.constants.QMK_FIRMWARE, 'data', 'templates', 'xap', data_templates_xap_subdir) + templates_dir = qmk.constants.QMK_FIRMWARE / 'data/templates/xap' / data_templates_xap_subdir j2 = Environment(loader=FileSystemLoader(templates_dir), autoescape=select_autoescape(), lstrip_blocks=True, trim_blocks=True) return j2 diff --git a/quantum/led_matrix/lighting_map.h b/quantum/led_matrix/lighting_map.h new file mode 100644 index 000000000000..0d087d867a17 --- /dev/null +++ b/quantum/led_matrix/lighting_map.h @@ -0,0 +1,263 @@ +// Copyright 2023 QMK +// SPDX-License-Identifier: GPL-2.0-or-later + +/******************************************************************************* + 88888888888 888 d8b .d888 d8b 888 d8b + 888 888 Y8P d88P" Y8P 888 Y8P + 888 888 888 888 + 888 88888b. 888 .d8888b 888888 888 888 .d88b. 888 .d8888b + 888 888 "88b 888 88K 888 888 888 d8P Y8b 888 88K + 888 888 888 888 "Y8888b. 888 888 888 88888888 888 "Y8888b. + 888 888 888 888 X88 888 888 888 Y8b. 888 X88 + 888 888 888 888 88888P' 888 888 888 "Y8888 888 88888P' + 888 888 + 888 888 + 888 888 + .d88b. .d88b. 88888b. .d88b. 888d888 8888b. 888888 .d88b. .d88888 + d88P"88b d8P Y8b 888 "88b d8P Y8b 888P" "88b 888 d8P Y8b d88" 888 + 888 888 88888888 888 888 88888888 888 .d888888 888 88888888 888 888 + Y88b 888 Y8b. 888 888 Y8b. 888 888 888 Y88b. Y8b. Y88b 888 + "Y88888 "Y8888 888 888 "Y8888 888 "Y888888 "Y888 "Y8888 "Y88888 + 888 + Y8b d88P + "Y88P" +*******************************************************************************/ + +#pragma once +// clang-format off +enum { ENABLED_LED_MATRIX_EFFECTS = 0 + | (1ULL << 0x00) + +#ifdef ENABLE_LED_MATRIX_ALPHAS_MODS + | (1ULL << 0x01) +#endif + +#ifdef ENABLE_LED_MATRIX_BREATHING + | (1ULL << 0x02) +#endif + +#ifdef ENABLE_LED_MATRIX_BAND + | (1ULL << 0x03) +#endif + +#ifdef ENABLE_LED_MATRIX_BAND_PINWHEEL + | (1ULL << 0x04) +#endif + +#ifdef ENABLE_LED_MATRIX_BAND_SPIRAL + | (1ULL << 0x05) +#endif + +#ifdef ENABLE_LED_MATRIX_CYCLE_LEFT_RIGHT + | (1ULL << 0x06) +#endif + +#ifdef ENABLE_LED_MATRIX_CYCLE_UP_DOWN + | (1ULL << 0x07) +#endif + +#ifdef ENABLE_LED_MATRIX_CYCLE_OUT_IN + | (1ULL << 0x08) +#endif + +#ifdef ENABLE_LED_MATRIX_DUAL_BEACON + | (1ULL << 0x09) +#endif + +#ifdef ENABLE_LED_MATRIX_WAVE_LEFT_RIGHT + | (1ULL << 0x0A) +#endif + +#ifdef ENABLE_LED_MATRIX_WAVE_UP_DOWN + | (1ULL << 0x0B) +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE + | (1ULL << 0x0C) +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE + | (1ULL << 0x0D) +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE + | (1ULL << 0x0E) +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_CROSS + | (1ULL << 0x0F) +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS + | (1ULL << 0x10) +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS + | (1ULL << 0x11) +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS + | (1ULL << 0x12) +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_SPLASH + | (1ULL << 0x13) +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_MULTISPLASH + | (1ULL << 0x14) +#endif +#endif +}; +static const uint8_t led_matrix_effect_map[][2] PROGMEM = { +{ 0x00, LED_MATRIX_SOLID}, + +#ifdef ENABLE_LED_MATRIX_ALPHAS_MODS +{ 0x01, LED_MATRIX_ALPHAS_MODS}, +#endif + +#ifdef ENABLE_LED_MATRIX_BREATHING +{ 0x02, LED_MATRIX_BREATHING}, +#endif + +#ifdef ENABLE_LED_MATRIX_BAND +{ 0x03, LED_MATRIX_BAND}, +#endif + +#ifdef ENABLE_LED_MATRIX_BAND_PINWHEEL +{ 0x04, LED_MATRIX_BAND_PINWHEEL}, +#endif + +#ifdef ENABLE_LED_MATRIX_BAND_SPIRAL +{ 0x05, LED_MATRIX_BAND_SPIRAL}, +#endif + +#ifdef ENABLE_LED_MATRIX_CYCLE_LEFT_RIGHT +{ 0x06, LED_MATRIX_CYCLE_LEFT_RIGHT}, +#endif + +#ifdef ENABLE_LED_MATRIX_CYCLE_UP_DOWN +{ 0x07, LED_MATRIX_CYCLE_UP_DOWN}, +#endif + +#ifdef ENABLE_LED_MATRIX_CYCLE_OUT_IN +{ 0x08, LED_MATRIX_CYCLE_OUT_IN}, +#endif + +#ifdef ENABLE_LED_MATRIX_DUAL_BEACON +{ 0x09, LED_MATRIX_DUAL_BEACON}, +#endif + +#ifdef ENABLE_LED_MATRIX_WAVE_LEFT_RIGHT +{ 0x0A, LED_MATRIX_WAVE_LEFT_RIGHT}, +#endif + +#ifdef ENABLE_LED_MATRIX_WAVE_UP_DOWN +{ 0x0B, LED_MATRIX_WAVE_UP_DOWN}, +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE +{ 0x0C, LED_MATRIX_SOLID_REACTIVE_SIMPLE}, +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE +{ 0x0D, LED_MATRIX_SOLID_REACTIVE_WIDE}, +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE +{ 0x0E, LED_MATRIX_SOLID_REACTIVE_MULTIWIDE}, +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_CROSS +{ 0x0F, LED_MATRIX_SOLID_REACTIVE_CROSS}, +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS +{ 0x10, LED_MATRIX_SOLID_REACTIVE_MULTICROSS}, +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS +{ 0x11, LED_MATRIX_SOLID_REACTIVE_NEXUS}, +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS +{ 0x12, LED_MATRIX_SOLID_REACTIVE_MULTINEXUS}, +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_SPLASH +{ 0x13, LED_MATRIX_SOLID_SPLASH}, +#endif +#endif + +#ifdef LED_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_LED_MATRIX_SOLID_MULTISPLASH +{ 0x14, LED_MATRIX_SOLID_MULTISPLASH}, +#endif +#endif +}; + +uint8_t led_matrix_effect_to_id(uint8_t val) { + for(uint8_t i = 0; i < ARRAY_SIZE(led_matrix_effect_map); i++) { + if (pgm_read_byte(&led_matrix_effect_map[i][1]) == val) + return pgm_read_byte(&led_matrix_effect_map[i][0]); + } + return 0xFF; +} + +uint8_t led_matrix_id_to_effect(uint8_t val) { + for(uint8_t i = 0; i < ARRAY_SIZE(led_matrix_effect_map); i++) { + if (pgm_read_byte(&led_matrix_effect_map[i][0]) == val) + return pgm_read_byte(&led_matrix_effect_map[i][1]); + } + return 0xFF; +} diff --git a/quantum/rgb_matrix/lighting_map.h b/quantum/rgb_matrix/lighting_map.h new file mode 100644 index 000000000000..249cf349be2a --- /dev/null +++ b/quantum/rgb_matrix/lighting_map.h @@ -0,0 +1,477 @@ +// Copyright 2023 QMK +// SPDX-License-Identifier: GPL-2.0-or-later + +/******************************************************************************* + 88888888888 888 d8b .d888 d8b 888 d8b + 888 888 Y8P d88P" Y8P 888 Y8P + 888 888 888 888 + 888 88888b. 888 .d8888b 888888 888 888 .d88b. 888 .d8888b + 888 888 "88b 888 88K 888 888 888 d8P Y8b 888 88K + 888 888 888 888 "Y8888b. 888 888 888 88888888 888 "Y8888b. + 888 888 888 888 X88 888 888 888 Y8b. 888 X88 + 888 888 888 888 88888P' 888 888 888 "Y8888 888 88888P' + 888 888 + 888 888 + 888 888 + .d88b. .d88b. 88888b. .d88b. 888d888 8888b. 888888 .d88b. .d88888 + d88P"88b d8P Y8b 888 "88b d8P Y8b 888P" "88b 888 d8P Y8b d88" 888 + 888 888 88888888 888 888 88888888 888 .d888888 888 88888888 888 888 + Y88b 888 Y8b. 888 888 Y8b. 888 888 888 Y88b. Y8b. Y88b 888 + "Y88888 "Y8888 888 888 "Y8888 888 "Y888888 "Y888 "Y8888 "Y88888 + 888 + Y8b d88P + "Y88P" +*******************************************************************************/ + +#pragma once +// clang-format off +enum { ENABLED_RGB_MATRIX_EFFECTS = 0 + | (1ULL << 0x00) + +#ifdef ENABLE_RGB_MATRIX_ALPHAS_MODS + | (1ULL << 0x01) +#endif + +#ifdef ENABLE_RGB_MATRIX_GRADIENT_UP_DOWN + | (1ULL << 0x02) +#endif + +#ifdef ENABLE_RGB_MATRIX_GRADIENT_LEFT_RIGHT + | (1ULL << 0x03) +#endif + +#ifdef ENABLE_RGB_MATRIX_BREATHING + | (1ULL << 0x04) +#endif + +#ifdef ENABLE_RGB_MATRIX_BAND_SAT + | (1ULL << 0x05) +#endif + +#ifdef ENABLE_RGB_MATRIX_BAND_VAL + | (1ULL << 0x06) +#endif + +#ifdef ENABLE_RGB_MATRIX_BAND_PINWHEEL_SAT + | (1ULL << 0x07) +#endif + +#ifdef ENABLE_RGB_MATRIX_BAND_PINWHEEL_VAL + | (1ULL << 0x08) +#endif + +#ifdef ENABLE_RGB_MATRIX_BAND_SPIRAL_SAT + | (1ULL << 0x09) +#endif + +#ifdef ENABLE_RGB_MATRIX_BAND_SPIRAL_VAL + | (1ULL << 0x0A) +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_ALL + | (1ULL << 0x0B) +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT + | (1ULL << 0x0C) +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_UP_DOWN + | (1ULL << 0x0D) +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_OUT_IN + | (1ULL << 0x0E) +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_OUT_IN_DUAL + | (1ULL << 0x0F) +#endif + +#ifdef ENABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON + | (1ULL << 0x10) +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_PINWHEEL + | (1ULL << 0x11) +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_SPIRAL + | (1ULL << 0x12) +#endif + +#ifdef ENABLE_RGB_MATRIX_DUAL_BEACON + | (1ULL << 0x13) +#endif + +#ifdef ENABLE_RGB_MATRIX_RAINBOW_BEACON + | (1ULL << 0x14) +#endif + +#ifdef ENABLE_RGB_MATRIX_RAINBOW_PINWHEELS + | (1ULL << 0x15) +#endif + +#ifdef ENABLE_RGB_MATRIX_RAINDROPS + | (1ULL << 0x16) +#endif + +#ifdef ENABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS + | (1ULL << 0x17) +#endif + +#ifdef ENABLE_RGB_MATRIX_HUE_BREATHING + | (1ULL << 0x18) +#endif + +#ifdef ENABLE_RGB_MATRIX_HUE_PENDULUM + | (1ULL << 0x19) +#endif + +#ifdef ENABLE_RGB_MATRIX_HUE_WAVE + | (1ULL << 0x1A) +#endif + +#ifdef ENABLE_RGB_MATRIX_PIXEL_FRACTAL + | (1ULL << 0x1B) +#endif + +#ifdef ENABLE_RGB_MATRIX_PIXEL_FLOW + | (1ULL << 0x1C) +#endif + +#ifdef ENABLE_RGB_MATRIX_PIXEL_RAIN + | (1ULL << 0x1D) +#endif + +#ifdef RGB_MATRIX_FRAMEBUFFER_EFFECTS + +#ifdef ENABLE_RGB_MATRIX_TYPING_HEATMAP + | (1ULL << 0x1E) +#endif +#endif + +#ifdef RGB_MATRIX_FRAMEBUFFER_EFFECTS + +#ifdef ENABLE_RGB_MATRIX_DIGITAL_RAIN + | (1ULL << 0x1F) +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE + | (1ULL << 0x20) +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE + | (1ULL << 0x21) +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_WIDE + | (1ULL << 0x22) +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE + | (1ULL << 0x23) +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_CROSS + | (1ULL << 0x24) +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTICROSS + | (1ULL << 0x25) +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_NEXUS + | (1ULL << 0x26) +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS + | (1ULL << 0x27) +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SPLASH + | (1ULL << 0x28) +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_MULTISPLASH + | (1ULL << 0x29) +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_SPLASH + | (1ULL << 0x2A) +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_MULTISPLASH + | (1ULL << 0x2B) +#endif +#endif +}; +static const uint8_t rgb_matrix_effect_map[][2] PROGMEM = { +{ 0x00, RGB_MATRIX_SOLID_COLOR}, + +#ifdef ENABLE_RGB_MATRIX_ALPHAS_MODS +{ 0x01, RGB_MATRIX_ALPHAS_MODS}, +#endif + +#ifdef ENABLE_RGB_MATRIX_GRADIENT_UP_DOWN +{ 0x02, RGB_MATRIX_GRADIENT_UP_DOWN}, +#endif + +#ifdef ENABLE_RGB_MATRIX_GRADIENT_LEFT_RIGHT +{ 0x03, RGB_MATRIX_GRADIENT_LEFT_RIGHT}, +#endif + +#ifdef ENABLE_RGB_MATRIX_BREATHING +{ 0x04, RGB_MATRIX_BREATHING}, +#endif + +#ifdef ENABLE_RGB_MATRIX_BAND_SAT +{ 0x05, RGB_MATRIX_BAND_SAT}, +#endif + +#ifdef ENABLE_RGB_MATRIX_BAND_VAL +{ 0x06, RGB_MATRIX_BAND_VAL}, +#endif + +#ifdef ENABLE_RGB_MATRIX_BAND_PINWHEEL_SAT +{ 0x07, RGB_MATRIX_BAND_PINWHEEL_SAT}, +#endif + +#ifdef ENABLE_RGB_MATRIX_BAND_PINWHEEL_VAL +{ 0x08, RGB_MATRIX_BAND_PINWHEEL_VAL}, +#endif + +#ifdef ENABLE_RGB_MATRIX_BAND_SPIRAL_SAT +{ 0x09, RGB_MATRIX_BAND_SPIRAL_SAT}, +#endif + +#ifdef ENABLE_RGB_MATRIX_BAND_SPIRAL_VAL +{ 0x0A, RGB_MATRIX_BAND_SPIRAL_VAL}, +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_ALL +{ 0x0B, RGB_MATRIX_CYCLE_ALL}, +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT +{ 0x0C, RGB_MATRIX_CYCLE_LEFT_RIGHT}, +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_UP_DOWN +{ 0x0D, RGB_MATRIX_CYCLE_UP_DOWN}, +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_OUT_IN +{ 0x0E, RGB_MATRIX_CYCLE_OUT_IN}, +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_OUT_IN_DUAL +{ 0x0F, RGB_MATRIX_CYCLE_OUT_IN_DUAL}, +#endif + +#ifdef ENABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON +{ 0x10, RGB_MATRIX_RAINBOW_MOVING_CHEVRON}, +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_PINWHEEL +{ 0x11, RGB_MATRIX_CYCLE_PINWHEEL}, +#endif + +#ifdef ENABLE_RGB_MATRIX_CYCLE_SPIRAL +{ 0x12, RGB_MATRIX_CYCLE_SPIRAL}, +#endif + +#ifdef ENABLE_RGB_MATRIX_DUAL_BEACON +{ 0x13, RGB_MATRIX_DUAL_BEACON}, +#endif + +#ifdef ENABLE_RGB_MATRIX_RAINBOW_BEACON +{ 0x14, RGB_MATRIX_RAINBOW_BEACON}, +#endif + +#ifdef ENABLE_RGB_MATRIX_RAINBOW_PINWHEELS +{ 0x15, RGB_MATRIX_RAINBOW_PINWHEELS}, +#endif + +#ifdef ENABLE_RGB_MATRIX_RAINDROPS +{ 0x16, RGB_MATRIX_RAINDROPS}, +#endif + +#ifdef ENABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS +{ 0x17, RGB_MATRIX_JELLYBEAN_RAINDROPS}, +#endif + +#ifdef ENABLE_RGB_MATRIX_HUE_BREATHING +{ 0x18, RGB_MATRIX_HUE_BREATHING}, +#endif + +#ifdef ENABLE_RGB_MATRIX_HUE_PENDULUM +{ 0x19, RGB_MATRIX_HUE_PENDULUM}, +#endif + +#ifdef ENABLE_RGB_MATRIX_HUE_WAVE +{ 0x1A, RGB_MATRIX_HUE_WAVE}, +#endif + +#ifdef ENABLE_RGB_MATRIX_PIXEL_FRACTAL +{ 0x1B, RGB_MATRIX_PIXEL_FRACTAL}, +#endif + +#ifdef ENABLE_RGB_MATRIX_PIXEL_FLOW +{ 0x1C, RGB_MATRIX_PIXEL_FLOW}, +#endif + +#ifdef ENABLE_RGB_MATRIX_PIXEL_RAIN +{ 0x1D, RGB_MATRIX_PIXEL_RAIN}, +#endif + +#ifdef RGB_MATRIX_FRAMEBUFFER_EFFECTS + +#ifdef ENABLE_RGB_MATRIX_TYPING_HEATMAP +{ 0x1E, RGB_MATRIX_TYPING_HEATMAP}, +#endif +#endif + +#ifdef RGB_MATRIX_FRAMEBUFFER_EFFECTS + +#ifdef ENABLE_RGB_MATRIX_DIGITAL_RAIN +{ 0x1F, RGB_MATRIX_DIGITAL_RAIN}, +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE +{ 0x20, RGB_MATRIX_SOLID_REACTIVE_SIMPLE}, +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE +{ 0x21, RGB_MATRIX_SOLID_REACTIVE}, +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_WIDE +{ 0x22, RGB_MATRIX_SOLID_REACTIVE_WIDE}, +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE +{ 0x23, RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE}, +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_CROSS +{ 0x24, RGB_MATRIX_SOLID_REACTIVE_CROSS}, +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTICROSS +{ 0x25, RGB_MATRIX_SOLID_REACTIVE_MULTICROSS}, +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_NEXUS +{ 0x26, RGB_MATRIX_SOLID_REACTIVE_NEXUS}, +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS +{ 0x27, RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS}, +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SPLASH +{ 0x28, RGB_MATRIX_SPLASH}, +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_MULTISPLASH +{ 0x29, RGB_MATRIX_MULTISPLASH}, +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_SPLASH +{ 0x2A, RGB_MATRIX_SOLID_SPLASH}, +#endif +#endif + +#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED + +#ifdef ENABLE_RGB_MATRIX_SOLID_MULTISPLASH +{ 0x2B, RGB_MATRIX_SOLID_MULTISPLASH}, +#endif +#endif +}; + +uint8_t rgb_matrix_effect_to_id(uint8_t val) { + for(uint8_t i = 0; i < ARRAY_SIZE(rgb_matrix_effect_map); i++) { + if (pgm_read_byte(&rgb_matrix_effect_map[i][1]) == val) + return pgm_read_byte(&rgb_matrix_effect_map[i][0]); + } + return 0xFF; +} + +uint8_t rgb_matrix_id_to_effect(uint8_t val) { + for(uint8_t i = 0; i < ARRAY_SIZE(rgb_matrix_effect_map); i++) { + if (pgm_read_byte(&rgb_matrix_effect_map[i][0]) == val) + return pgm_read_byte(&rgb_matrix_effect_map[i][1]); + } + return 0xFF; +} diff --git a/quantum/rgblight/lighting_map.h b/quantum/rgblight/lighting_map.h new file mode 100644 index 000000000000..7d14a51a5b29 --- /dev/null +++ b/quantum/rgblight/lighting_map.h @@ -0,0 +1,377 @@ +// Copyright 2023 QMK +// SPDX-License-Identifier: GPL-2.0-or-later + +/******************************************************************************* + 88888888888 888 d8b .d888 d8b 888 d8b + 888 888 Y8P d88P" Y8P 888 Y8P + 888 888 888 888 + 888 88888b. 888 .d8888b 888888 888 888 .d88b. 888 .d8888b + 888 888 "88b 888 88K 888 888 888 d8P Y8b 888 88K + 888 888 888 888 "Y8888b. 888 888 888 88888888 888 "Y8888b. + 888 888 888 888 X88 888 888 888 Y8b. 888 X88 + 888 888 888 888 88888P' 888 888 888 "Y8888 888 88888P' + 888 888 + 888 888 + 888 888 + .d88b. .d88b. 88888b. .d88b. 888d888 8888b. 888888 .d88b. .d88888 + d88P"88b d8P Y8b 888 "88b d8P Y8b 888P" "88b 888 d8P Y8b d88" 888 + 888 888 88888888 888 888 88888888 888 .d888888 888 88888888 888 888 + Y88b 888 Y8b. 888 888 Y8b. 888 888 888 Y88b. Y8b. Y88b 888 + "Y88888 "Y8888 888 888 "Y8888 888 "Y888888 "Y888 "Y8888 "Y88888 + 888 + Y8b d88P + "Y88P" +*******************************************************************************/ + +#pragma once +// clang-format off +enum { ENABLED_RGBLIGHT_EFFECTS = 0 + | (1ULL << 0x00) + +#ifdef RGBLIGHT_EFFECT_BREATHING + | (1ULL << 0x01) +#endif + +#ifdef RGBLIGHT_EFFECT_BREATHING + | (1ULL << 0x02) +#endif + +#ifdef RGBLIGHT_EFFECT_BREATHING + | (1ULL << 0x03) +#endif + +#ifdef RGBLIGHT_EFFECT_BREATHING + | (1ULL << 0x04) +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD + | (1ULL << 0x05) +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD + | (1ULL << 0x06) +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD + | (1ULL << 0x07) +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL + | (1ULL << 0x08) +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL + | (1ULL << 0x09) +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL + | (1ULL << 0x0A) +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL + | (1ULL << 0x0B) +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL + | (1ULL << 0x0C) +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL + | (1ULL << 0x0D) +#endif + +#ifdef RGBLIGHT_EFFECT_SNAKE + | (1ULL << 0x0E) +#endif + +#ifdef RGBLIGHT_EFFECT_SNAKE + | (1ULL << 0x0F) +#endif + +#ifdef RGBLIGHT_EFFECT_SNAKE + | (1ULL << 0x10) +#endif + +#ifdef RGBLIGHT_EFFECT_SNAKE + | (1ULL << 0x11) +#endif + +#ifdef RGBLIGHT_EFFECT_SNAKE + | (1ULL << 0x12) +#endif + +#ifdef RGBLIGHT_EFFECT_SNAKE + | (1ULL << 0x13) +#endif + +#ifdef RGBLIGHT_EFFECT_KNIGHT + | (1ULL << 0x14) +#endif + +#ifdef RGBLIGHT_EFFECT_KNIGHT + | (1ULL << 0x15) +#endif + +#ifdef RGBLIGHT_EFFECT_KNIGHT + | (1ULL << 0x16) +#endif + +#ifdef RGBLIGHT_EFFECT_CHRISTMAS + | (1ULL << 0x17) +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + | (1ULL << 0x18) +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + | (1ULL << 0x19) +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + | (1ULL << 0x1A) +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + | (1ULL << 0x1B) +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + | (1ULL << 0x1C) +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + | (1ULL << 0x1D) +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + | (1ULL << 0x1E) +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + | (1ULL << 0x1F) +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + | (1ULL << 0x20) +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT + | (1ULL << 0x21) +#endif + +#ifdef RGBLIGHT_EFFECT_RGB_TEST + | (1ULL << 0x22) +#endif + +#ifdef RGBLIGHT_EFFECT_ALTERNATING + | (1ULL << 0x23) +#endif + +#ifdef RGBLIGHT_EFFECT_TWINKLE + | (1ULL << 0x24) +#endif + +#ifdef RGBLIGHT_EFFECT_TWINKLE + | (1ULL << 0x25) +#endif + +#ifdef RGBLIGHT_EFFECT_TWINKLE + | (1ULL << 0x26) +#endif + +#ifdef RGBLIGHT_EFFECT_TWINKLE + | (1ULL << 0x27) +#endif + +#ifdef RGBLIGHT_EFFECT_TWINKLE + | (1ULL << 0x28) +#endif + +#ifdef RGBLIGHT_EFFECT_TWINKLE + | (1ULL << 0x29) +#endif +}; +static const uint8_t rgblight_effect_map[][2] PROGMEM = { +{ 0x00, RGBLIGHT_MODE_STATIC_LIGHT}, + +#ifdef RGBLIGHT_EFFECT_BREATHING +{ 0x01, RGBLIGHT_MODE_BREATHING}, +#endif + +#ifdef RGBLIGHT_EFFECT_BREATHING +{ 0x02, RGBLIGHT_MODE_BREATHING + 1}, +#endif + +#ifdef RGBLIGHT_EFFECT_BREATHING +{ 0x03, RGBLIGHT_MODE_BREATHING + 2}, +#endif + +#ifdef RGBLIGHT_EFFECT_BREATHING +{ 0x04, RGBLIGHT_MODE_BREATHING + 3}, +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD +{ 0x05, RGBLIGHT_MODE_RAINBOW_MOOD}, +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD +{ 0x06, RGBLIGHT_MODE_RAINBOW_MOOD + 1}, +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD +{ 0x07, RGBLIGHT_MODE_RAINBOW_MOOD + 2}, +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL +{ 0x08, RGBLIGHT_MODE_RAINBOW_SWIRL}, +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL +{ 0x09, RGBLIGHT_MODE_RAINBOW_SWIRL + 1}, +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL +{ 0x0A, RGBLIGHT_MODE_RAINBOW_SWIRL + 2}, +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL +{ 0x0B, RGBLIGHT_MODE_RAINBOW_SWIRL + 3}, +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL +{ 0x0C, RGBLIGHT_MODE_RAINBOW_SWIRL + 4}, +#endif + +#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL +{ 0x0D, RGBLIGHT_MODE_RAINBOW_SWIRL + 5}, +#endif + +#ifdef RGBLIGHT_EFFECT_SNAKE +{ 0x0E, RGBLIGHT_MODE_SNAKE}, +#endif + +#ifdef RGBLIGHT_EFFECT_SNAKE +{ 0x0F, RGBLIGHT_MODE_SNAKE + 1}, +#endif + +#ifdef RGBLIGHT_EFFECT_SNAKE +{ 0x10, RGBLIGHT_MODE_SNAKE + 2}, +#endif + +#ifdef RGBLIGHT_EFFECT_SNAKE +{ 0x11, RGBLIGHT_MODE_SNAKE + 3}, +#endif + +#ifdef RGBLIGHT_EFFECT_SNAKE +{ 0x12, RGBLIGHT_MODE_SNAKE + 4}, +#endif + +#ifdef RGBLIGHT_EFFECT_SNAKE +{ 0x13, RGBLIGHT_MODE_SNAKE + 5}, +#endif + +#ifdef RGBLIGHT_EFFECT_KNIGHT +{ 0x14, RGBLIGHT_MODE_KNIGHT}, +#endif + +#ifdef RGBLIGHT_EFFECT_KNIGHT +{ 0x15, RGBLIGHT_MODE_KNIGHT + 1}, +#endif + +#ifdef RGBLIGHT_EFFECT_KNIGHT +{ 0x16, RGBLIGHT_MODE_KNIGHT + 2}, +#endif + +#ifdef RGBLIGHT_EFFECT_CHRISTMAS +{ 0x17, RGBLIGHT_MODE_CHRISTMAS}, +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT +{ 0x18, RGBLIGHT_MODE_STATIC_GRADIENT}, +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT +{ 0x19, RGBLIGHT_MODE_STATIC_GRADIENT + 1}, +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT +{ 0x1A, RGBLIGHT_MODE_STATIC_GRADIENT + 2}, +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT +{ 0x1B, RGBLIGHT_MODE_STATIC_GRADIENT + 3}, +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT +{ 0x1C, RGBLIGHT_MODE_STATIC_GRADIENT + 4}, +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT +{ 0x1D, RGBLIGHT_MODE_STATIC_GRADIENT + 5}, +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT +{ 0x1E, RGBLIGHT_MODE_STATIC_GRADIENT + 6}, +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT +{ 0x1F, RGBLIGHT_MODE_STATIC_GRADIENT + 7}, +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT +{ 0x20, RGBLIGHT_MODE_STATIC_GRADIENT + 8}, +#endif + +#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT +{ 0x21, RGBLIGHT_MODE_STATIC_GRADIENT + 9}, +#endif + +#ifdef RGBLIGHT_EFFECT_RGB_TEST +{ 0x22, RGBLIGHT_MODE_RGB_TEST}, +#endif + +#ifdef RGBLIGHT_EFFECT_ALTERNATING +{ 0x23, RGBLIGHT_MODE_ALTERNATING}, +#endif + +#ifdef RGBLIGHT_EFFECT_TWINKLE +{ 0x24, RGBLIGHT_MODE_TWINKLE}, +#endif + +#ifdef RGBLIGHT_EFFECT_TWINKLE +{ 0x25, RGBLIGHT_MODE_TWINKLE + 1}, +#endif + +#ifdef RGBLIGHT_EFFECT_TWINKLE +{ 0x26, RGBLIGHT_MODE_TWINKLE + 2}, +#endif + +#ifdef RGBLIGHT_EFFECT_TWINKLE +{ 0x27, RGBLIGHT_MODE_TWINKLE + 3}, +#endif + +#ifdef RGBLIGHT_EFFECT_TWINKLE +{ 0x28, RGBLIGHT_MODE_TWINKLE + 4}, +#endif + +#ifdef RGBLIGHT_EFFECT_TWINKLE +{ 0x29, RGBLIGHT_MODE_TWINKLE + 5}, +#endif +}; + +uint8_t rgblight_effect_to_id(uint8_t val) { + for(uint8_t i = 0; i < ARRAY_SIZE(rgblight_effect_map); i++) { + if (pgm_read_byte(&rgblight_effect_map[i][1]) == val) + return pgm_read_byte(&rgblight_effect_map[i][0]); + } + return 0xFF; +} + +uint8_t rgblight_id_to_effect(uint8_t val) { + for(uint8_t i = 0; i < ARRAY_SIZE(rgblight_effect_map); i++) { + if (pgm_read_byte(&rgblight_effect_map[i][0]) == val) + return pgm_read_byte(&rgblight_effect_map[i][1]); + } + return 0xFF; +} diff --git a/quantum/xap/handlers/lighting.c b/quantum/xap/handlers/lighting.c index fae3dfe211af..5ce7d412695d 100644 --- a/quantum/xap/handlers/lighting.c +++ b/quantum/xap/handlers/lighting.c @@ -57,14 +57,14 @@ bool xap_respond_save_backlight_config(xap_token_t token, const void *data, size extern rgblight_config_t rgblight_config; -uint8_t rgblight2xap(uint8_t val); -uint8_t xap2rgblight(uint8_t val); +uint8_t rgblight_effect_to_id(uint8_t val); +uint8_t rgblight_id_to_effect(uint8_t val); bool xap_respond_get_rgblight_config(xap_token_t token, const void *data, size_t length) { xap_route_lighting_rgblight_get_config_t ret; ret.enable = rgblight_config.enable; - ret.mode = rgblight2xap(rgblight_config.mode); + ret.mode = rgblight_effect_to_id(rgblight_config.mode); ret.hue = rgblight_config.hue; ret.sat = rgblight_config.sat; ret.val = rgblight_config.val; @@ -80,7 +80,7 @@ bool xap_respond_set_rgblight_config(xap_token_t token, const void *data, size_t xap_route_lighting_rgblight_set_config_arg_t *arg = (xap_route_lighting_rgblight_set_config_arg_t *)data; - uint8_t mode = xap2rgblight(arg->mode); + uint8_t mode = rgblight_id_to_effect(arg->mode); if (mode == INVALID_EFFECT) { return false; } @@ -105,8 +105,8 @@ bool xap_respond_save_rgblight_config(xap_token_t token, const void *data, size_ extern rgb_config_t rgb_matrix_config; -uint8_t rgb_matrix2xap(uint8_t val); -uint8_t xap2rgb_matrix(uint8_t val); +uint8_t rgb_matrix_effect_to_id(uint8_t val); +uint8_t rgb_matrix_id_to_effect(uint8_t val); void rgb_matrix_enabled_noeeprom(bool val) { val ? rgb_matrix_enable_noeeprom() : rgb_matrix_disable_noeeprom(); @@ -116,7 +116,7 @@ bool xap_respond_get_rgb_matrix_config(xap_token_t token, const void *data, size xap_route_lighting_rgb_matrix_get_config_t ret; ret.enable = rgb_matrix_config.enable; - ret.mode = rgb_matrix2xap(rgb_matrix_config.mode); + ret.mode = rgb_matrix_effect_to_id(rgb_matrix_config.mode); ret.hue = rgb_matrix_config.hsv.h; ret.sat = rgb_matrix_config.hsv.s; ret.val = rgb_matrix_config.hsv.v; @@ -133,7 +133,7 @@ bool xap_respond_set_rgb_matrix_config(xap_token_t token, const void *data, size xap_route_lighting_rgb_matrix_set_config_arg_t *arg = (xap_route_lighting_rgb_matrix_set_config_arg_t *)data; - uint8_t mode = xap2rgb_matrix(arg->mode); + uint8_t mode = rgb_matrix_id_to_effect(arg->mode); if (mode == INVALID_EFFECT) { return false; } diff --git a/quantum/xap/lighting_map.h b/quantum/xap/lighting_map.h deleted file mode 100644 index f44965ddf092..000000000000 --- a/quantum/xap/lighting_map.h +++ /dev/null @@ -1,1093 +0,0 @@ -// Copyright 2023 QMK -// SPDX-License-Identifier: GPL-2.0-or-later - -/******************************************************************************* - 88888888888 888 d8b .d888 d8b 888 d8b - 888 888 Y8P d88P" Y8P 888 Y8P - 888 888 888 888 - 888 88888b. 888 .d8888b 888888 888 888 .d88b. 888 .d8888b - 888 888 "88b 888 88K 888 888 888 d8P Y8b 888 88K - 888 888 888 888 "Y8888b. 888 888 888 88888888 888 "Y8888b. - 888 888 888 888 X88 888 888 888 Y8b. 888 X88 - 888 888 888 888 88888P' 888 888 888 "Y8888 888 88888P' - 888 888 - 888 888 - 888 888 - .d88b. .d88b. 88888b. .d88b. 888d888 8888b. 888888 .d88b. .d88888 - d88P"88b d8P Y8b 888 "88b d8P Y8b 888P" "88b 888 d8P Y8b d88" 888 - 888 888 88888888 888 888 88888888 888 .d888888 888 88888888 888 888 - Y88b 888 Y8b. 888 888 Y8b. 888 888 888 Y88b. Y8b. Y88b 888 - "Y88888 "Y8888 888 888 "Y8888 888 "Y888888 "Y888 "Y8888 "Y88888 - 888 - Y8b d88P - "Y88P" -*******************************************************************************/ - -#pragma once -// clang-format off - -#define RGBLIGHT_EFFECT_STATIC_LIGHT -#define ENABLE_RGB_MATRIX_SOLID_COLOR -#define ENABLE_LED_MATRIX_SOLID - -#ifdef RGBLIGHT_ENABLE -static const uint8_t rgblight_effect_map[][2] PROGMEM = { - -#ifdef RGBLIGHT_EFFECT_STATIC_LIGHT - { 0x00, RGBLIGHT_MODE_STATIC_LIGHT}, -#endif - -#ifdef RGBLIGHT_EFFECT_BREATHING - { 0x01, RGBLIGHT_MODE_BREATHING}, -#endif - -#ifdef RGBLIGHT_EFFECT_BREATHING - { 0x02, RGBLIGHT_MODE_BREATHING + 1}, -#endif - -#ifdef RGBLIGHT_EFFECT_BREATHING - { 0x03, RGBLIGHT_MODE_BREATHING + 2}, -#endif - -#ifdef RGBLIGHT_EFFECT_BREATHING - { 0x04, RGBLIGHT_MODE_BREATHING + 3}, -#endif - -#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD - { 0x05, RGBLIGHT_MODE_RAINBOW_MOOD}, -#endif - -#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD - { 0x06, RGBLIGHT_MODE_RAINBOW_MOOD + 1}, -#endif - -#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD - { 0x07, RGBLIGHT_MODE_RAINBOW_MOOD + 2}, -#endif - -#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL - { 0x08, RGBLIGHT_MODE_RAINBOW_SWIRL}, -#endif - -#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL - { 0x09, RGBLIGHT_MODE_RAINBOW_SWIRL + 1}, -#endif - -#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL - { 0x0A, RGBLIGHT_MODE_RAINBOW_SWIRL + 2}, -#endif - -#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL - { 0x0B, RGBLIGHT_MODE_RAINBOW_SWIRL + 3}, -#endif - -#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL - { 0x0C, RGBLIGHT_MODE_RAINBOW_SWIRL + 4}, -#endif - -#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL - { 0x0D, RGBLIGHT_MODE_RAINBOW_SWIRL + 5}, -#endif - -#ifdef RGBLIGHT_EFFECT_SNAKE - { 0x0E, RGBLIGHT_MODE_SNAKE}, -#endif - -#ifdef RGBLIGHT_EFFECT_SNAKE - { 0x0F, RGBLIGHT_MODE_SNAKE + 1}, -#endif - -#ifdef RGBLIGHT_EFFECT_SNAKE - { 0x10, RGBLIGHT_MODE_SNAKE + 2}, -#endif - -#ifdef RGBLIGHT_EFFECT_SNAKE - { 0x11, RGBLIGHT_MODE_SNAKE + 3}, -#endif - -#ifdef RGBLIGHT_EFFECT_SNAKE - { 0x12, RGBLIGHT_MODE_SNAKE + 4}, -#endif - -#ifdef RGBLIGHT_EFFECT_SNAKE - { 0x13, RGBLIGHT_MODE_SNAKE + 5}, -#endif - -#ifdef RGBLIGHT_EFFECT_KNIGHT - { 0x14, RGBLIGHT_MODE_KNIGHT}, -#endif - -#ifdef RGBLIGHT_EFFECT_KNIGHT - { 0x15, RGBLIGHT_MODE_KNIGHT + 1}, -#endif - -#ifdef RGBLIGHT_EFFECT_KNIGHT - { 0x16, RGBLIGHT_MODE_KNIGHT + 2}, -#endif - -#ifdef RGBLIGHT_EFFECT_CHRISTMAS - { 0x17, RGBLIGHT_MODE_CHRISTMAS}, -#endif - -#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT - { 0x18, RGBLIGHT_MODE_STATIC_GRADIENT}, -#endif - -#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT - { 0x19, RGBLIGHT_MODE_STATIC_GRADIENT + 1}, -#endif - -#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT - { 0x1A, RGBLIGHT_MODE_STATIC_GRADIENT + 2}, -#endif - -#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT - { 0x1B, RGBLIGHT_MODE_STATIC_GRADIENT + 3}, -#endif - -#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT - { 0x1C, RGBLIGHT_MODE_STATIC_GRADIENT + 4}, -#endif - -#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT - { 0x1D, RGBLIGHT_MODE_STATIC_GRADIENT + 5}, -#endif - -#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT - { 0x1E, RGBLIGHT_MODE_STATIC_GRADIENT + 6}, -#endif - -#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT - { 0x1F, RGBLIGHT_MODE_STATIC_GRADIENT + 7}, -#endif - -#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT - { 0x20, RGBLIGHT_MODE_STATIC_GRADIENT + 8}, -#endif - -#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT - { 0x21, RGBLIGHT_MODE_STATIC_GRADIENT + 9}, -#endif - -#ifdef RGBLIGHT_EFFECT_RGB_TEST - { 0x22, RGBLIGHT_MODE_RGB_TEST}, -#endif - -#ifdef RGBLIGHT_EFFECT_ALTERNATING - { 0x23, RGBLIGHT_MODE_ALTERNATING}, -#endif - -#ifdef RGBLIGHT_EFFECT_TWINKLE - { 0x24, RGBLIGHT_MODE_TWINKLE}, -#endif - -#ifdef RGBLIGHT_EFFECT_TWINKLE - { 0x25, RGBLIGHT_MODE_TWINKLE + 1}, -#endif - -#ifdef RGBLIGHT_EFFECT_TWINKLE - { 0x26, RGBLIGHT_MODE_TWINKLE + 2}, -#endif - -#ifdef RGBLIGHT_EFFECT_TWINKLE - { 0x27, RGBLIGHT_MODE_TWINKLE + 3}, -#endif - -#ifdef RGBLIGHT_EFFECT_TWINKLE - { 0x28, RGBLIGHT_MODE_TWINKLE + 4}, -#endif - -#ifdef RGBLIGHT_EFFECT_TWINKLE - { 0x29, RGBLIGHT_MODE_TWINKLE + 5}, -#endif -}; - -uint8_t rgblight2xap(uint8_t val) { - for(uint8_t i = 0; i < ARRAY_SIZE(rgblight_effect_map); i++) { - if (pgm_read_byte(&rgblight_effect_map[i][1]) == val) - return pgm_read_byte(&rgblight_effect_map[i][0]); - } - return 0xFF; -} - -uint8_t xap2rgblight(uint8_t val) { - for(uint8_t i = 0; i < ARRAY_SIZE(rgblight_effect_map); i++) { - if (pgm_read_byte(&rgblight_effect_map[i][0]) == val) - return pgm_read_byte(&rgblight_effect_map[i][1]); - } - return 0xFF; -} -enum { ENABLED_RGBLIGHT_EFFECTS = 0 - -#ifdef RGBLIGHT_EFFECT_STATIC_LIGHT - | (1ULL << 0x00) -#endif - -#ifdef RGBLIGHT_EFFECT_BREATHING - | (1ULL << 0x01) -#endif - -#ifdef RGBLIGHT_EFFECT_BREATHING - | (1ULL << 0x02) -#endif - -#ifdef RGBLIGHT_EFFECT_BREATHING - | (1ULL << 0x03) -#endif - -#ifdef RGBLIGHT_EFFECT_BREATHING - | (1ULL << 0x04) -#endif - -#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD - | (1ULL << 0x05) -#endif - -#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD - | (1ULL << 0x06) -#endif - -#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD - | (1ULL << 0x07) -#endif - -#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL - | (1ULL << 0x08) -#endif - -#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL - | (1ULL << 0x09) -#endif - -#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL - | (1ULL << 0x0A) -#endif - -#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL - | (1ULL << 0x0B) -#endif - -#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL - | (1ULL << 0x0C) -#endif - -#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL - | (1ULL << 0x0D) -#endif - -#ifdef RGBLIGHT_EFFECT_SNAKE - | (1ULL << 0x0E) -#endif - -#ifdef RGBLIGHT_EFFECT_SNAKE - | (1ULL << 0x0F) -#endif - -#ifdef RGBLIGHT_EFFECT_SNAKE - | (1ULL << 0x10) -#endif - -#ifdef RGBLIGHT_EFFECT_SNAKE - | (1ULL << 0x11) -#endif - -#ifdef RGBLIGHT_EFFECT_SNAKE - | (1ULL << 0x12) -#endif - -#ifdef RGBLIGHT_EFFECT_SNAKE - | (1ULL << 0x13) -#endif - -#ifdef RGBLIGHT_EFFECT_KNIGHT - | (1ULL << 0x14) -#endif - -#ifdef RGBLIGHT_EFFECT_KNIGHT - | (1ULL << 0x15) -#endif - -#ifdef RGBLIGHT_EFFECT_KNIGHT - | (1ULL << 0x16) -#endif - -#ifdef RGBLIGHT_EFFECT_CHRISTMAS - | (1ULL << 0x17) -#endif - -#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT - | (1ULL << 0x18) -#endif - -#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT - | (1ULL << 0x19) -#endif - -#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT - | (1ULL << 0x1A) -#endif - -#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT - | (1ULL << 0x1B) -#endif - -#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT - | (1ULL << 0x1C) -#endif - -#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT - | (1ULL << 0x1D) -#endif - -#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT - | (1ULL << 0x1E) -#endif - -#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT - | (1ULL << 0x1F) -#endif - -#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT - | (1ULL << 0x20) -#endif - -#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT - | (1ULL << 0x21) -#endif - -#ifdef RGBLIGHT_EFFECT_RGB_TEST - | (1ULL << 0x22) -#endif - -#ifdef RGBLIGHT_EFFECT_ALTERNATING - | (1ULL << 0x23) -#endif - -#ifdef RGBLIGHT_EFFECT_TWINKLE - | (1ULL << 0x24) -#endif - -#ifdef RGBLIGHT_EFFECT_TWINKLE - | (1ULL << 0x25) -#endif - -#ifdef RGBLIGHT_EFFECT_TWINKLE - | (1ULL << 0x26) -#endif - -#ifdef RGBLIGHT_EFFECT_TWINKLE - | (1ULL << 0x27) -#endif - -#ifdef RGBLIGHT_EFFECT_TWINKLE - | (1ULL << 0x28) -#endif - -#ifdef RGBLIGHT_EFFECT_TWINKLE - | (1ULL << 0x29) -#endif -}; -#endif //RGBLIGHT_ENABLE -#ifdef RGB_MATRIX_ENABLE -static const uint8_t rgb_matrix_effect_map[][2] PROGMEM = { - -#ifdef ENABLE_RGB_MATRIX_SOLID_COLOR - { 0x00, RGB_MATRIX_SOLID_COLOR}, -#endif - -#ifdef ENABLE_RGB_MATRIX_ALPHAS_MODS - { 0x01, RGB_MATRIX_ALPHAS_MODS}, -#endif - -#ifdef ENABLE_RGB_MATRIX_GRADIENT_UP_DOWN - { 0x02, RGB_MATRIX_GRADIENT_UP_DOWN}, -#endif - -#ifdef ENABLE_RGB_MATRIX_GRADIENT_LEFT_RIGHT - { 0x03, RGB_MATRIX_GRADIENT_LEFT_RIGHT}, -#endif - -#ifdef ENABLE_RGB_MATRIX_BREATHING - { 0x04, RGB_MATRIX_BREATHING}, -#endif - -#ifdef ENABLE_RGB_MATRIX_BAND_SAT - { 0x05, RGB_MATRIX_BAND_SAT}, -#endif - -#ifdef ENABLE_RGB_MATRIX_BAND_VAL - { 0x06, RGB_MATRIX_BAND_VAL}, -#endif - -#ifdef ENABLE_RGB_MATRIX_BAND_PINWHEEL_SAT - { 0x07, RGB_MATRIX_BAND_PINWHEEL_SAT}, -#endif - -#ifdef ENABLE_RGB_MATRIX_BAND_PINWHEEL_VAL - { 0x08, RGB_MATRIX_BAND_PINWHEEL_VAL}, -#endif - -#ifdef ENABLE_RGB_MATRIX_BAND_SPIRAL_SAT - { 0x09, RGB_MATRIX_BAND_SPIRAL_SAT}, -#endif - -#ifdef ENABLE_RGB_MATRIX_BAND_SPIRAL_VAL - { 0x0A, RGB_MATRIX_BAND_SPIRAL_VAL}, -#endif - -#ifdef ENABLE_RGB_MATRIX_CYCLE_ALL - { 0x0B, RGB_MATRIX_CYCLE_ALL}, -#endif - -#ifdef ENABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT - { 0x0C, RGB_MATRIX_CYCLE_LEFT_RIGHT}, -#endif - -#ifdef ENABLE_RGB_MATRIX_CYCLE_UP_DOWN - { 0x0D, RGB_MATRIX_CYCLE_UP_DOWN}, -#endif - -#ifdef ENABLE_RGB_MATRIX_CYCLE_OUT_IN - { 0x0E, RGB_MATRIX_CYCLE_OUT_IN}, -#endif - -#ifdef ENABLE_RGB_MATRIX_CYCLE_OUT_IN_DUAL - { 0x0F, RGB_MATRIX_CYCLE_OUT_IN_DUAL}, -#endif - -#ifdef ENABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON - { 0x10, RGB_MATRIX_RAINBOW_MOVING_CHEVRON}, -#endif - -#ifdef ENABLE_RGB_MATRIX_CYCLE_PINWHEEL - { 0x11, RGB_MATRIX_CYCLE_PINWHEEL}, -#endif - -#ifdef ENABLE_RGB_MATRIX_CYCLE_SPIRAL - { 0x12, RGB_MATRIX_CYCLE_SPIRAL}, -#endif - -#ifdef ENABLE_RGB_MATRIX_DUAL_BEACON - { 0x13, RGB_MATRIX_DUAL_BEACON}, -#endif - -#ifdef ENABLE_RGB_MATRIX_RAINBOW_BEACON - { 0x14, RGB_MATRIX_RAINBOW_BEACON}, -#endif - -#ifdef ENABLE_RGB_MATRIX_RAINBOW_PINWHEELS - { 0x15, RGB_MATRIX_RAINBOW_PINWHEELS}, -#endif - -#ifdef ENABLE_RGB_MATRIX_RAINDROPS - { 0x16, RGB_MATRIX_RAINDROPS}, -#endif - -#ifdef ENABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS - { 0x17, RGB_MATRIX_JELLYBEAN_RAINDROPS}, -#endif - -#ifdef ENABLE_RGB_MATRIX_HUE_BREATHING - { 0x18, RGB_MATRIX_HUE_BREATHING}, -#endif - -#ifdef ENABLE_RGB_MATRIX_HUE_PENDULUM - { 0x19, RGB_MATRIX_HUE_PENDULUM}, -#endif - -#ifdef ENABLE_RGB_MATRIX_HUE_WAVE - { 0x1A, RGB_MATRIX_HUE_WAVE}, -#endif - -#ifdef ENABLE_RGB_MATRIX_PIXEL_FRACTAL - { 0x1B, RGB_MATRIX_PIXEL_FRACTAL}, -#endif - -#ifdef ENABLE_RGB_MATRIX_PIXEL_FLOW - { 0x1C, RGB_MATRIX_PIXEL_FLOW}, -#endif - -#ifdef ENABLE_RGB_MATRIX_PIXEL_RAIN - { 0x1D, RGB_MATRIX_PIXEL_RAIN}, -#endif - -#ifdef RGB_MATRIX_FRAMEBUFFER_EFFECTS - -#ifdef ENABLE_RGB_MATRIX_TYPING_HEATMAP - { 0x1E, RGB_MATRIX_TYPING_HEATMAP}, -#endif -#endif - -#ifdef RGB_MATRIX_FRAMEBUFFER_EFFECTS - -#ifdef ENABLE_RGB_MATRIX_DIGITAL_RAIN - { 0x1F, RGB_MATRIX_DIGITAL_RAIN}, -#endif -#endif - -#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE - { 0x20, RGB_MATRIX_SOLID_REACTIVE_SIMPLE}, -#endif -#endif - -#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE - { 0x21, RGB_MATRIX_SOLID_REACTIVE}, -#endif -#endif - -#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_WIDE - { 0x22, RGB_MATRIX_SOLID_REACTIVE_WIDE}, -#endif -#endif - -#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE - { 0x23, RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE}, -#endif -#endif - -#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_CROSS - { 0x24, RGB_MATRIX_SOLID_REACTIVE_CROSS}, -#endif -#endif - -#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTICROSS - { 0x25, RGB_MATRIX_SOLID_REACTIVE_MULTICROSS}, -#endif -#endif - -#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_NEXUS - { 0x26, RGB_MATRIX_SOLID_REACTIVE_NEXUS}, -#endif -#endif - -#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS - { 0x27, RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS}, -#endif -#endif - -#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_RGB_MATRIX_SPLASH - { 0x28, RGB_MATRIX_SPLASH}, -#endif -#endif - -#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_RGB_MATRIX_MULTISPLASH - { 0x29, RGB_MATRIX_MULTISPLASH}, -#endif -#endif - -#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_RGB_MATRIX_SOLID_SPLASH - { 0x2A, RGB_MATRIX_SOLID_SPLASH}, -#endif -#endif - -#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_RGB_MATRIX_SOLID_MULTISPLASH - { 0x2B, RGB_MATRIX_SOLID_MULTISPLASH}, -#endif -#endif -}; - -uint8_t rgb_matrix2xap(uint8_t val) { - for(uint8_t i = 0; i < ARRAY_SIZE(rgb_matrix_effect_map); i++) { - if (pgm_read_byte(&rgb_matrix_effect_map[i][1]) == val) - return pgm_read_byte(&rgb_matrix_effect_map[i][0]); - } - return 0xFF; -} - -uint8_t xap2rgb_matrix(uint8_t val) { - for(uint8_t i = 0; i < ARRAY_SIZE(rgb_matrix_effect_map); i++) { - if (pgm_read_byte(&rgb_matrix_effect_map[i][0]) == val) - return pgm_read_byte(&rgb_matrix_effect_map[i][1]); - } - return 0xFF; -} -enum { ENABLED_RGB_MATRIX_EFFECTS = 0 - -#ifdef ENABLE_RGB_MATRIX_SOLID_COLOR - | (1ULL << 0x00) -#endif - -#ifdef ENABLE_RGB_MATRIX_ALPHAS_MODS - | (1ULL << 0x01) -#endif - -#ifdef ENABLE_RGB_MATRIX_GRADIENT_UP_DOWN - | (1ULL << 0x02) -#endif - -#ifdef ENABLE_RGB_MATRIX_GRADIENT_LEFT_RIGHT - | (1ULL << 0x03) -#endif - -#ifdef ENABLE_RGB_MATRIX_BREATHING - | (1ULL << 0x04) -#endif - -#ifdef ENABLE_RGB_MATRIX_BAND_SAT - | (1ULL << 0x05) -#endif - -#ifdef ENABLE_RGB_MATRIX_BAND_VAL - | (1ULL << 0x06) -#endif - -#ifdef ENABLE_RGB_MATRIX_BAND_PINWHEEL_SAT - | (1ULL << 0x07) -#endif - -#ifdef ENABLE_RGB_MATRIX_BAND_PINWHEEL_VAL - | (1ULL << 0x08) -#endif - -#ifdef ENABLE_RGB_MATRIX_BAND_SPIRAL_SAT - | (1ULL << 0x09) -#endif - -#ifdef ENABLE_RGB_MATRIX_BAND_SPIRAL_VAL - | (1ULL << 0x0A) -#endif - -#ifdef ENABLE_RGB_MATRIX_CYCLE_ALL - | (1ULL << 0x0B) -#endif - -#ifdef ENABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT - | (1ULL << 0x0C) -#endif - -#ifdef ENABLE_RGB_MATRIX_CYCLE_UP_DOWN - | (1ULL << 0x0D) -#endif - -#ifdef ENABLE_RGB_MATRIX_CYCLE_OUT_IN - | (1ULL << 0x0E) -#endif - -#ifdef ENABLE_RGB_MATRIX_CYCLE_OUT_IN_DUAL - | (1ULL << 0x0F) -#endif - -#ifdef ENABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON - | (1ULL << 0x10) -#endif - -#ifdef ENABLE_RGB_MATRIX_CYCLE_PINWHEEL - | (1ULL << 0x11) -#endif - -#ifdef ENABLE_RGB_MATRIX_CYCLE_SPIRAL - | (1ULL << 0x12) -#endif - -#ifdef ENABLE_RGB_MATRIX_DUAL_BEACON - | (1ULL << 0x13) -#endif - -#ifdef ENABLE_RGB_MATRIX_RAINBOW_BEACON - | (1ULL << 0x14) -#endif - -#ifdef ENABLE_RGB_MATRIX_RAINBOW_PINWHEELS - | (1ULL << 0x15) -#endif - -#ifdef ENABLE_RGB_MATRIX_RAINDROPS - | (1ULL << 0x16) -#endif - -#ifdef ENABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS - | (1ULL << 0x17) -#endif - -#ifdef ENABLE_RGB_MATRIX_HUE_BREATHING - | (1ULL << 0x18) -#endif - -#ifdef ENABLE_RGB_MATRIX_HUE_PENDULUM - | (1ULL << 0x19) -#endif - -#ifdef ENABLE_RGB_MATRIX_HUE_WAVE - | (1ULL << 0x1A) -#endif - -#ifdef ENABLE_RGB_MATRIX_PIXEL_FRACTAL - | (1ULL << 0x1B) -#endif - -#ifdef ENABLE_RGB_MATRIX_PIXEL_FLOW - | (1ULL << 0x1C) -#endif - -#ifdef ENABLE_RGB_MATRIX_PIXEL_RAIN - | (1ULL << 0x1D) -#endif - -#ifdef RGB_MATRIX_FRAMEBUFFER_EFFECTS - -#ifdef ENABLE_RGB_MATRIX_TYPING_HEATMAP - | (1ULL << 0x1E) -#endif -#endif - -#ifdef RGB_MATRIX_FRAMEBUFFER_EFFECTS - -#ifdef ENABLE_RGB_MATRIX_DIGITAL_RAIN - | (1ULL << 0x1F) -#endif -#endif - -#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE - | (1ULL << 0x20) -#endif -#endif - -#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE - | (1ULL << 0x21) -#endif -#endif - -#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_WIDE - | (1ULL << 0x22) -#endif -#endif - -#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE - | (1ULL << 0x23) -#endif -#endif - -#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_CROSS - | (1ULL << 0x24) -#endif -#endif - -#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTICROSS - | (1ULL << 0x25) -#endif -#endif - -#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_NEXUS - | (1ULL << 0x26) -#endif -#endif - -#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS - | (1ULL << 0x27) -#endif -#endif - -#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_RGB_MATRIX_SPLASH - | (1ULL << 0x28) -#endif -#endif - -#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_RGB_MATRIX_MULTISPLASH - | (1ULL << 0x29) -#endif -#endif - -#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_RGB_MATRIX_SOLID_SPLASH - | (1ULL << 0x2A) -#endif -#endif - -#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_RGB_MATRIX_SOLID_MULTISPLASH - | (1ULL << 0x2B) -#endif -#endif -}; -#endif //RGB_MATRIX_ENABLE -#ifdef LED_MATRIX_ENABLE -static const uint8_t led_matrix_effect_map[][2] PROGMEM = { - -#ifdef ENABLE_LED_MATRIX_SOLID - { 0x00, LED_MATRIX_SOLID}, -#endif - -#ifdef ENABLE_LED_MATRIX_ALPHAS_MODS - { 0x01, LED_MATRIX_ALPHAS_MODS}, -#endif - -#ifdef ENABLE_LED_MATRIX_BREATHING - { 0x02, LED_MATRIX_BREATHING}, -#endif - -#ifdef ENABLE_LED_MATRIX_BAND - { 0x03, LED_MATRIX_BAND}, -#endif - -#ifdef ENABLE_LED_MATRIX_BAND_PINWHEEL - { 0x04, LED_MATRIX_BAND_PINWHEEL}, -#endif - -#ifdef ENABLE_LED_MATRIX_BAND_SPIRAL - { 0x05, LED_MATRIX_BAND_SPIRAL}, -#endif - -#ifdef ENABLE_LED_MATRIX_CYCLE_LEFT_RIGHT - { 0x06, LED_MATRIX_CYCLE_LEFT_RIGHT}, -#endif - -#ifdef ENABLE_LED_MATRIX_CYCLE_UP_DOWN - { 0x07, LED_MATRIX_CYCLE_UP_DOWN}, -#endif - -#ifdef ENABLE_LED_MATRIX_CYCLE_OUT_IN - { 0x08, LED_MATRIX_CYCLE_OUT_IN}, -#endif - -#ifdef ENABLE_LED_MATRIX_DUAL_BEACON - { 0x09, LED_MATRIX_DUAL_BEACON}, -#endif - -#ifdef ENABLE_LED_MATRIX_WAVE_LEFT_RIGHT - { 0x0A, LED_MATRIX_WAVE_LEFT_RIGHT}, -#endif - -#ifdef ENABLE_LED_MATRIX_WAVE_UP_DOWN - { 0x0B, LED_MATRIX_WAVE_UP_DOWN}, -#endif - -#ifdef LED_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE - { 0x0C, LED_MATRIX_SOLID_REACTIVE_SIMPLE}, -#endif -#endif - -#ifdef LED_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE - { 0x0D, LED_MATRIX_SOLID_REACTIVE_WIDE}, -#endif -#endif - -#ifdef LED_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE - { 0x0E, LED_MATRIX_SOLID_REACTIVE_MULTIWIDE}, -#endif -#endif - -#ifdef LED_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_CROSS - { 0x0F, LED_MATRIX_SOLID_REACTIVE_CROSS}, -#endif -#endif - -#ifdef LED_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS - { 0x10, LED_MATRIX_SOLID_REACTIVE_MULTICROSS}, -#endif -#endif - -#ifdef LED_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS - { 0x11, LED_MATRIX_SOLID_REACTIVE_NEXUS}, -#endif -#endif - -#ifdef LED_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS - { 0x12, LED_MATRIX_SOLID_REACTIVE_MULTINEXUS}, -#endif -#endif - -#ifdef LED_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_LED_MATRIX_SOLID_SPLASH - { 0x13, LED_MATRIX_SOLID_SPLASH}, -#endif -#endif - -#ifdef LED_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_LED_MATRIX_SOLID_MULTISPLASH - { 0x14, LED_MATRIX_SOLID_MULTISPLASH}, -#endif -#endif -}; - -uint8_t led_matrix2xap(uint8_t val) { - for(uint8_t i = 0; i < ARRAY_SIZE(led_matrix_effect_map); i++) { - if (pgm_read_byte(&led_matrix_effect_map[i][1]) == val) - return pgm_read_byte(&led_matrix_effect_map[i][0]); - } - return 0xFF; -} - -uint8_t xap2led_matrix(uint8_t val) { - for(uint8_t i = 0; i < ARRAY_SIZE(led_matrix_effect_map); i++) { - if (pgm_read_byte(&led_matrix_effect_map[i][0]) == val) - return pgm_read_byte(&led_matrix_effect_map[i][1]); - } - return 0xFF; -} -enum { ENABLED_LED_MATRIX_EFFECTS = 0 - -#ifdef ENABLE_LED_MATRIX_SOLID - | (1ULL << 0x00) -#endif - -#ifdef ENABLE_LED_MATRIX_ALPHAS_MODS - | (1ULL << 0x01) -#endif - -#ifdef ENABLE_LED_MATRIX_BREATHING - | (1ULL << 0x02) -#endif - -#ifdef ENABLE_LED_MATRIX_BAND - | (1ULL << 0x03) -#endif - -#ifdef ENABLE_LED_MATRIX_BAND_PINWHEEL - | (1ULL << 0x04) -#endif - -#ifdef ENABLE_LED_MATRIX_BAND_SPIRAL - | (1ULL << 0x05) -#endif - -#ifdef ENABLE_LED_MATRIX_CYCLE_LEFT_RIGHT - | (1ULL << 0x06) -#endif - -#ifdef ENABLE_LED_MATRIX_CYCLE_UP_DOWN - | (1ULL << 0x07) -#endif - -#ifdef ENABLE_LED_MATRIX_CYCLE_OUT_IN - | (1ULL << 0x08) -#endif - -#ifdef ENABLE_LED_MATRIX_DUAL_BEACON - | (1ULL << 0x09) -#endif - -#ifdef ENABLE_LED_MATRIX_WAVE_LEFT_RIGHT - | (1ULL << 0x0A) -#endif - -#ifdef ENABLE_LED_MATRIX_WAVE_UP_DOWN - | (1ULL << 0x0B) -#endif - -#ifdef LED_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE - | (1ULL << 0x0C) -#endif -#endif - -#ifdef LED_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE - | (1ULL << 0x0D) -#endif -#endif - -#ifdef LED_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE - | (1ULL << 0x0E) -#endif -#endif - -#ifdef LED_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_CROSS - | (1ULL << 0x0F) -#endif -#endif - -#ifdef LED_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS - | (1ULL << 0x10) -#endif -#endif - -#ifdef LED_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS - | (1ULL << 0x11) -#endif -#endif - -#ifdef LED_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS - | (1ULL << 0x12) -#endif -#endif - -#ifdef LED_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_LED_MATRIX_SOLID_SPLASH - | (1ULL << 0x13) -#endif -#endif - -#ifdef LED_MATRIX_KEYREACTIVE_ENABLED - -#ifdef ENABLE_LED_MATRIX_SOLID_MULTISPLASH - | (1ULL << 0x14) -#endif -#endif -}; -#endif //LED_MATRIX_ENABLE - diff --git a/util/regen.sh b/util/regen.sh index ab03018893c0..cfa472299300 100755 --- a/util/regen.sh +++ b/util/regen.sh @@ -1,6 +1,9 @@ #!/bin/bash set -e +qmk generate-lighting-map -f rgblight -o quantum/rgblight/lighting_map.h +qmk generate-lighting-map -f rgb_matrix -o quantum/rgb_matrix/lighting_map.h +qmk generate-lighting-map -f led_matrix -o quantum/led_matrix/lighting_map.h qmk generate-rgb-breathe-table -o quantum/rgblight/rgblight_breathe_table.h qmk generate-keycodes --version latest -o quantum/keycodes.h qmk generate-keycodes-tests --version latest -o tests/test_common/keycode_table.cpp From e953b72835f732c66631615654fc8ca334fdb035 Mon Sep 17 00:00:00 2001 From: zvecr Date: Sun, 26 Mar 2023 03:15:41 +0100 Subject: [PATCH 185/203] Fix lint errors --- lib/python/qmk/cli/generate/lighting_map.py | 2 +- lib/python/qmk/xap/common.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/python/qmk/cli/generate/lighting_map.py b/lib/python/qmk/cli/generate/lighting_map.py index 9a44085ba4ac..98606348709b 100644 --- a/lib/python/qmk/cli/generate/lighting_map.py +++ b/lib/python/qmk/cli/generate/lighting_map.py @@ -105,7 +105,7 @@ def _append_lighting_bit_field(lines, feature, spec): def _append_lighting_mapping(lines, feature): - """Generate lookup table and bit-field of effect + """Generate lookup table and bit-field of effect """ spec = load_lighting_spec(feature) diff --git a/lib/python/qmk/xap/common.py b/lib/python/qmk/xap/common.py index 827e4162f6ca..03d188804c42 100755 --- a/lib/python/qmk/xap/common.py +++ b/lib/python/qmk/xap/common.py @@ -9,7 +9,7 @@ import qmk.constants from qmk.git import git_get_version from qmk.lighting import load_lighting_spec -from qmk.json_schema import json_load, validate, merge_ordered_dicts +from qmk.json_schema import validate, merge_ordered_dicts from qmk.makefile import parse_rules_mk_file from qmk.decorators import lru_cache from qmk.keymap import locate_keymap From 79d031d27e8ab5f1615fb22cc3a1c966b537db4b Mon Sep 17 00:00:00 2001 From: zvecr Date: Sun, 26 Mar 2023 03:54:33 +0100 Subject: [PATCH 186/203] Fix lighting map --- quantum/xap/xap.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/quantum/xap/xap.c b/quantum/xap/xap.c index f3ac73d90233..491f27c8d771 100644 --- a/quantum/xap/xap.c +++ b/quantum/xap/xap.c @@ -18,7 +18,16 @@ #include #include "secure.h" -#include "lighting_map.h" +#ifdef RGBLIGHT_ENABLE +# include "rgblight/lighting_map.h" +#endif +#ifdef RGB_MATRIX_ENABLE +# include "rgb_matrix/lighting_map.h" +#endif +#ifdef LED_MATRIX_ENABLE +# include "led_matrix/lighting_map.h" +#endif + #include "config_blob_gz.h" bool get_config_blob_chunk(uint16_t offset, uint8_t *data, uint8_t data_len) { if (offset >= CONFIG_BLOB_GZ_LEN) { From 13f57c6d45e321be67dcc984a7702af9579e19c6 Mon Sep 17 00:00:00 2001 From: zvecr Date: Sun, 26 Mar 2023 23:51:27 +0100 Subject: [PATCH 187/203] Partial generation of execute helpers --- .../templates/xap/firmware/xap_generated.h.j2 | 6 +++- .../xap/firmware/xap_generated.inl.j2 | 30 +++++++++++++--- quantum/xap/handlers/audio.c | 12 ++----- quantum/xap/handlers/core.c | 21 ++++------- quantum/xap/handlers/lighting.c | 36 +++++-------------- quantum/xap/handlers/remapping.c | 33 +++-------------- 6 files changed, 55 insertions(+), 83 deletions(-) diff --git a/data/templates/xap/firmware/xap_generated.h.j2 b/data/templates/xap/firmware/xap_generated.h.j2 index 167c8776e9be..22f0942106eb 100755 --- a/data/templates/xap/firmware/xap_generated.h.j2 +++ b/data/templates/xap/firmware/xap_generated.h.j2 @@ -72,8 +72,12 @@ typedef struct { } __attribute__((__packed__)) {{ this_prefix_lc | to_snake | lower }}_arg_t; _Static_assert(sizeof({{ this_prefix_lc | to_snake | lower }}_arg_t) == {{ data.request_struct_length }}, "{{ this_prefix_lc | to_snake | lower }}_arg_t needs to be {{ data.request_struct_length }} bytes in size"); {% elif 'request_type' in data %} +{% if '[' in data.request_type %} +typedef struct __attribute__((__packed__)) { {{ data.request_type | type_to_c('x') }}; } {{ this_prefix_lc }}_arg_t; +{% else %} typedef {{ data.request_type | type_to_c(this_prefix_lc+'_arg_t') }}; {% endif %} +{% endif %} {% if 'return_struct_members' in data %} typedef struct { @@ -88,8 +92,8 @@ typedef struct __attribute__((__packed__)) { {{ data.return_type | type_to_c('x' {% else %} typedef {{ data.return_type | type_to_c(this_prefix_lc+'_t') }}; {% endif %} - {% endif %} + {{ export_route_types(this_prefix_lc, data) }} {% endfor %} {% endif %} diff --git a/data/templates/xap/firmware/xap_generated.inl.j2 b/data/templates/xap/firmware/xap_generated.inl.j2 index b682a25306f6..8fe89bdba6a4 100755 --- a/data/templates/xap/firmware/xap_generated.inl.j2 +++ b/data/templates/xap/firmware/xap_generated.inl.j2 @@ -32,18 +32,40 @@ void {{ xap.broadcast_messages.define_prefix | lower }}_{{ data.define | to_snak //////////////////////////////////////////////////////////////////////////////// // Decl -{% macro export_route_declaration(container) %} +{% macro export_route_declaration(prefix, container) %} {% if 'routes' in container %} {% for route, data in container.routes | dictsort %} +{% set this_prefix_uc = (prefix + '_' + data.define) | upper %} +{% set this_prefix_lc = this_prefix_uc | lower %} {% if 'return_execute' in data %} -bool xap_respond_{{ data.return_execute }}(xap_token_t token, const uint8_t *data, size_t data_len); + +{% if 'request_struct_members' in data %} +{% set arg_type = ( this_prefix_lc | to_snake | lower ) + '_arg_t' %} +{% set arg_var = arg_type + ' arg' %} +{% elif 'request_type' in data %} +{% set arg_type = data.request_type | type_to_c() %} +{% set arg_var = data.request_type | type_to_c('arg') %} +{% endif %} + +__attribute__((weak)) bool xap_execute_{{ data.return_execute }}(xap_token_t token{% if arg_type %}, {{ arg_type }}{{ '*' if 'xap_route' in arg_type else '' }}{% endif %}) { return false; } +__attribute__((weak)) bool xap_respond_{{ data.return_execute }}(xap_token_t token, const uint8_t *data, size_t data_len) { +{% if arg_type %} + if (data_len != sizeof({{ arg_type }})) { + return false; + } + {{ arg_var }}; + memcpy(&arg, data, sizeof({{ arg_type }})); +{% endif %} + + return xap_execute_{{ data.return_execute }}(token{% if arg_type %}, {{ '&' if 'xap_route' in arg_type else '' }}arg{% endif %}); +} {% endif %} -{{ export_route_declaration(data) }} +{{ export_route_declaration(this_prefix_lc, data) }} {% endfor %} {% endif %} {% endmacro %} -{{ export_route_declaration(xap) }} +{{ export_route_declaration('xap_route', xap) }} //////////////////////////////////////////////////////////////////////////////// // Data diff --git a/quantum/xap/handlers/audio.c b/quantum/xap/handlers/audio.c index b06ec0311cc4..d973943ead1f 100644 --- a/quantum/xap/handlers/audio.c +++ b/quantum/xap/handlers/audio.c @@ -9,7 +9,7 @@ extern audio_config_t audio_config; -bool xap_respond_get_audio_config(xap_token_t token, const void *data, size_t length) { +bool xap_execute_get_audio_config(xap_token_t token) { xap_route_audio_get_config_t ret; ret.enable = audio_config.enable; @@ -18,20 +18,14 @@ bool xap_respond_get_audio_config(xap_token_t token, const void *data, size_t le return xap_respond_data(token, &ret, sizeof(ret)); } -bool xap_respond_set_audio_config(xap_token_t token, const void *data, size_t length) { - if (length != sizeof(xap_route_audio_set_config_arg_t)) { - return false; - } - - xap_route_audio_set_config_arg_t *arg = (xap_route_audio_set_config_arg_t *)data; - +bool xap_execute_set_audio_config(xap_token_t token, xap_route_audio_set_config_arg_t* arg) { audio_config.enable = arg->enable; audio_config.clicky_enable = arg->clicky_enable; return xap_respond_success(token); } -bool xap_respond_save_audio_config(xap_token_t token, const void *data, size_t length) { +bool xap_execute_save_audio_config(xap_token_t token) { eeconfig_update_audio_current(); return xap_respond_success(token); diff --git a/quantum/xap/handlers/core.c b/quantum/xap/handlers/core.c index 7969dc79759f..47fbaec1d2ab 100644 --- a/quantum/xap/handlers/core.c +++ b/quantum/xap/handlers/core.c @@ -5,14 +5,7 @@ #include "xap.h" #include "hardware_id.h" -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; - memcpy(&offset, data, sizeof(uint16_t)); - +bool xap_execute_get_config_blob_chunk(xap_token_t token, uint16_t offset) { xap_route_qmk_config_blob_chunk_t ret = {0}; bool get_config_blob_chunk(uint16_t offset, uint8_t * data, uint8_t data_len); @@ -23,23 +16,23 @@ bool xap_respond_get_config_blob_chunk(xap_token_t token, const void *data, size return xap_respond_data(token, &ret, sizeof(ret)); } -bool xap_respond_secure_status(xap_token_t token, const void *data, size_t length) { +bool xap_execute_secure_status(xap_token_t token) { 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) { +bool xap_execute_secure_unlock(xap_token_t token) { secure_request_unlock(); return xap_respond_success(token); } -bool xap_respond_secure_lock(xap_token_t token, const void *data, size_t length) { +bool xap_execute_secure_lock(xap_token_t token) { secure_lock(); return xap_respond_success(token); } #ifdef BOOTLOADER_JUMP_SUPPORTED -bool xap_respond_request_bootloader_jump(xap_token_t token, const void *data, size_t length) { +bool xap_execute_request_bootloader(xap_token_t token) { uint8_t ret = secure_is_unlocked(); // TODO: post to deferred queue so this request can return? @@ -50,7 +43,7 @@ bool xap_respond_request_bootloader_jump(xap_token_t token, const void *data, si #endif #ifndef NO_RESET -bool xap_respond_request_eeprom_reset(xap_token_t token, const void *data, size_t length) { +bool xap_execute_request_eeprom_reset(xap_token_t token) { uint8_t ret = secure_is_unlocked(); // TODO: post to deferred queue so this request can return? @@ -61,7 +54,7 @@ bool xap_respond_request_eeprom_reset(xap_token_t token, const void *data, size_ } #endif -bool xap_respond_get_hardware_id(xap_token_t token, const void *data, size_t length) { +bool xap_execute_get_hardware_id(xap_token_t token) { hardware_id_t ret = get_hardware_id(); return xap_respond_data(token, &ret, sizeof(ret)); } \ No newline at end of file diff --git a/quantum/xap/handlers/lighting.c b/quantum/xap/handlers/lighting.c index 5ce7d412695d..03d8aa0802c0 100644 --- a/quantum/xap/handlers/lighting.c +++ b/quantum/xap/handlers/lighting.c @@ -11,7 +11,7 @@ extern backlight_config_t backlight_config; -bool xap_respond_get_backlight_config(xap_token_t token, const void *data, size_t length) { +bool xap_execute_get_backlight_config(xap_token_t token) { xap_route_lighting_backlight_get_config_t ret; ret.enable = backlight_config.enable; @@ -21,13 +21,7 @@ bool xap_respond_get_backlight_config(xap_token_t token, const void *data, size_ return xap_respond_data(token, &ret, sizeof(ret)); } -bool xap_respond_set_backlight_config(xap_token_t token, const void *data, size_t length) { - if (length != sizeof(xap_route_lighting_backlight_set_config_arg_t)) { - return false; - } - - xap_route_lighting_backlight_set_config_arg_t *arg = (xap_route_lighting_backlight_set_config_arg_t *)data; - +bool xap_execute_set_backlight_config(xap_token_t token, xap_route_lighting_backlight_set_config_arg_t* arg) { if (arg->enable) { backlight_level_noeeprom(arg->val); } else { @@ -45,7 +39,7 @@ bool xap_respond_set_backlight_config(xap_token_t token, const void *data, size_ return xap_respond_success(token); } -bool xap_respond_save_backlight_config(xap_token_t token, const void *data, size_t length) { +bool xap_execute_save_backlight_config(xap_token_t token) { eeconfig_update_backlight_current(); return xap_respond_success(token); @@ -60,7 +54,7 @@ extern rgblight_config_t rgblight_config; uint8_t rgblight_effect_to_id(uint8_t val); uint8_t rgblight_id_to_effect(uint8_t val); -bool xap_respond_get_rgblight_config(xap_token_t token, const void *data, size_t length) { +bool xap_execute_get_rgblight_config(xap_token_t token) { xap_route_lighting_rgblight_get_config_t ret; ret.enable = rgblight_config.enable; @@ -73,13 +67,7 @@ bool xap_respond_get_rgblight_config(xap_token_t token, const void *data, size_t return xap_respond_data(token, &ret, sizeof(ret)); } -bool xap_respond_set_rgblight_config(xap_token_t token, const void *data, size_t length) { - if (length != sizeof(xap_route_lighting_rgblight_set_config_arg_t)) { - return false; - } - - xap_route_lighting_rgblight_set_config_arg_t *arg = (xap_route_lighting_rgblight_set_config_arg_t *)data; - +bool xap_execute_set_rgblight_config(xap_token_t token, xap_route_lighting_rgblight_set_config_arg_t* arg) { uint8_t mode = rgblight_id_to_effect(arg->mode); if (mode == INVALID_EFFECT) { return false; @@ -93,7 +81,7 @@ bool xap_respond_set_rgblight_config(xap_token_t token, const void *data, size_t return xap_respond_success(token); } -bool xap_respond_save_rgblight_config(xap_token_t token, const void *data, size_t length) { +bool xap_execute_save_rgblight_config(xap_token_t token) { eeconfig_update_rgblight_current(); return xap_respond_success(token); @@ -112,7 +100,7 @@ void rgb_matrix_enabled_noeeprom(bool val) { val ? rgb_matrix_enable_noeeprom() : rgb_matrix_disable_noeeprom(); } -bool xap_respond_get_rgb_matrix_config(xap_token_t token, const void *data, size_t length) { +bool xap_execute_get_rgb_matrix_config(xap_token_t token) { xap_route_lighting_rgb_matrix_get_config_t ret; ret.enable = rgb_matrix_config.enable; @@ -126,13 +114,7 @@ bool xap_respond_get_rgb_matrix_config(xap_token_t token, const void *data, size return xap_respond_data(token, &ret, sizeof(ret)); } -bool xap_respond_set_rgb_matrix_config(xap_token_t token, const void *data, size_t length) { - if (length != sizeof(xap_route_lighting_rgb_matrix_set_config_arg_t)) { - return false; - } - - xap_route_lighting_rgb_matrix_set_config_arg_t *arg = (xap_route_lighting_rgb_matrix_set_config_arg_t *)data; - +bool xap_execute_set_rgb_matrix_config(xap_token_t token, xap_route_lighting_rgb_matrix_set_config_arg_t* arg) { uint8_t mode = rgb_matrix_id_to_effect(arg->mode); if (mode == INVALID_EFFECT) { return false; @@ -147,7 +129,7 @@ bool xap_respond_set_rgb_matrix_config(xap_token_t token, const void *data, size return xap_respond_success(token); } -bool xap_respond_save_rgb_matrix_config(xap_token_t token, const void *data, size_t length) { +bool xap_execute_save_rgb_matrix_config(xap_token_t token) { eeconfig_update_rgb_matrix(); return xap_respond_success(token); diff --git a/quantum/xap/handlers/remapping.c b/quantum/xap/handlers/remapping.c index 6378dfc80c2e..8a77e69eae0b 100644 --- a/quantum/xap/handlers/remapping.c +++ b/quantum/xap/handlers/remapping.c @@ -10,18 +10,13 @@ # define keymap_max_layer_count() keymap_layer_count() #endif -bool xap_respond_keymap_get_layer_count(xap_token_t token, const void *data, size_t length) { +bool xap_execute_keymap_get_layer_count_impl(xap_token_t token) { uint8_t ret = keymap_max_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; +bool xap_execute_get_keymap_keycode_impl(xap_token_t token, const xap_route_keymap_get_keymap_keycode_arg_t *arg) { if (arg->layer >= keymap_max_layer_count()) { return false; } @@ -31,13 +26,7 @@ bool xap_respond_get_keymap_keycode(xap_token_t token, const void *data, size_t } #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; - +bool xap_execute_get_encoder_keycode_impl(xap_token_t token, const xap_route_keymap_get_encoder_keycode_arg_t *arg) { if (arg->layer >= keymap_max_layer_count()) { return false; } @@ -48,13 +37,7 @@ bool xap_respond_get_encoder_keycode(xap_token_t token, const void *data, size_t #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; - +bool xap_execute_dynamic_keymap_set_keycode_impl(xap_token_t token, const xap_route_remapping_set_keymap_keycode_arg_t *arg) { if (arg->layer >= keymap_max_layer_count()) { return false; } @@ -65,13 +48,7 @@ bool xap_respond_dynamic_keymap_set_keycode(xap_token_t token, const void *data, #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; - +bool xap_execute_dynamic_encoder_set_keycode_impl(xap_token_t token, const xap_route_remapping_set_encoder_keycode_arg_t *arg) { if (arg->layer >= keymap_max_layer_count()) { return false; } From 8388187e203dee878cce7782dfd7b8001c5540ca Mon Sep 17 00:00:00 2001 From: zvecr Date: Mon, 27 Mar 2023 01:31:14 +0100 Subject: [PATCH 188/203] whoops --- quantum/xap/handlers/remapping.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/quantum/xap/handlers/remapping.c b/quantum/xap/handlers/remapping.c index 8a77e69eae0b..b0913b803af1 100644 --- a/quantum/xap/handlers/remapping.c +++ b/quantum/xap/handlers/remapping.c @@ -10,13 +10,13 @@ # define keymap_max_layer_count() keymap_layer_count() #endif -bool xap_execute_keymap_get_layer_count_impl(xap_token_t token) { +bool xap_execute_keymap_get_layer_count(xap_token_t token) { uint8_t ret = keymap_max_layer_count(); return xap_respond_data(token, &ret, sizeof(ret)); } -bool xap_execute_get_keymap_keycode_impl(xap_token_t token, const xap_route_keymap_get_keymap_keycode_arg_t *arg) { +bool xap_execute_get_keymap_keycode(xap_token_t token, const xap_route_keymap_get_keymap_keycode_arg_t *arg) { if (arg->layer >= keymap_max_layer_count()) { return false; } @@ -26,7 +26,7 @@ bool xap_execute_get_keymap_keycode_impl(xap_token_t token, const xap_route_keym } #if ((defined(ENCODER_MAP_ENABLE))) -bool xap_execute_get_encoder_keycode_impl(xap_token_t token, const xap_route_keymap_get_encoder_keycode_arg_t *arg) { +bool xap_execute_get_encoder_keycode(xap_token_t token, const xap_route_keymap_get_encoder_keycode_arg_t *arg) { if (arg->layer >= keymap_max_layer_count()) { return false; } @@ -37,7 +37,7 @@ bool xap_execute_get_encoder_keycode_impl(xap_token_t token, const xap_route_key #endif #if ((defined(DYNAMIC_KEYMAP_ENABLE))) -bool xap_execute_dynamic_keymap_set_keycode_impl(xap_token_t token, const xap_route_remapping_set_keymap_keycode_arg_t *arg) { +bool xap_execute_dynamic_keymap_set_keycode(xap_token_t token, const xap_route_remapping_set_keymap_keycode_arg_t *arg) { if (arg->layer >= keymap_max_layer_count()) { return false; } @@ -48,7 +48,7 @@ bool xap_execute_dynamic_keymap_set_keycode_impl(xap_token_t token, const xap_ro #endif #if ((defined(DYNAMIC_KEYMAP_ENABLE) && defined(ENCODER_MAP_ENABLE))) -bool xap_execute_dynamic_encoder_set_keycode_impl(xap_token_t token, const xap_route_remapping_set_encoder_keycode_arg_t *arg) { +bool xap_execute_dynamic_encoder_set_keycode(xap_token_t token, const xap_route_remapping_set_encoder_keycode_arg_t *arg) { if (arg->layer >= keymap_max_layer_count()) { return false; } From f3c48f100a32096896c90654685dea4484d725e5 Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Thu, 30 Mar 2023 08:49:10 +1100 Subject: [PATCH 189/203] Fixup compilation failures. --- quantum/xap/handlers/remapping.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/quantum/xap/handlers/remapping.c b/quantum/xap/handlers/remapping.c index b0913b803af1..5887a1abcaf1 100644 --- a/quantum/xap/handlers/remapping.c +++ b/quantum/xap/handlers/remapping.c @@ -3,8 +3,10 @@ #include "quantum.h" #include "xap.h" +#include "keymap_introspection.h" #ifdef DYNAMIC_KEYMAP_ENABLE +# include "dynamic_keymap.h" # define keymap_max_layer_count() DYNAMIC_KEYMAP_LAYER_COUNT #else # define keymap_max_layer_count() keymap_layer_count() @@ -15,7 +17,6 @@ bool xap_execute_keymap_get_layer_count(xap_token_t token) { return xap_respond_data(token, &ret, sizeof(ret)); } - bool xap_execute_get_keymap_keycode(xap_token_t token, const xap_route_keymap_get_keymap_keycode_arg_t *arg) { if (arg->layer >= keymap_max_layer_count()) { return false; @@ -56,4 +57,4 @@ bool xap_execute_dynamic_encoder_set_keycode(xap_token_t token, const xap_route_ dynamic_keymap_set_encoder(arg->layer, arg->encoder, arg->clockwise, arg->keycode); return xap_respond_success(token); } -#endif \ No newline at end of file +#endif From f5e8c10be8dcaa3f2ff53db4b21b27cdf6f7730e Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 23 May 2023 00:31:20 +0100 Subject: [PATCH 190/203] Fix compilation on older gcc versions --- data/templates/xap/firmware/xap_generated.inl.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/templates/xap/firmware/xap_generated.inl.j2 b/data/templates/xap/firmware/xap_generated.inl.j2 index 8fe89bdba6a4..208b977a97c7 100755 --- a/data/templates/xap/firmware/xap_generated.inl.j2 +++ b/data/templates/xap/firmware/xap_generated.inl.j2 @@ -47,7 +47,7 @@ void {{ xap.broadcast_messages.define_prefix | lower }}_{{ data.define | to_snak {% set arg_var = data.request_type | type_to_c('arg') %} {% endif %} -__attribute__((weak)) bool xap_execute_{{ data.return_execute }}(xap_token_t token{% if arg_type %}, {{ arg_type }}{{ '*' if 'xap_route' in arg_type else '' }}{% endif %}) { return false; } +__attribute__((weak)) bool xap_execute_{{ data.return_execute }}(xap_token_t token{% if arg_type %}, {{ (arg_type + '* arg') if 'xap_route' in arg_type else arg_var }}{% endif %}) { return false; } __attribute__((weak)) bool xap_respond_{{ data.return_execute }}(xap_token_t token, const uint8_t *data, size_t data_len) { {% if arg_type %} if (data_len != sizeof({{ arg_type }})) { From 8bb1dd7645e8da8329f2dbfff838db74973b6915 Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Thu, 6 Jul 2023 02:55:18 +0100 Subject: [PATCH 191/203] KEYMAP_OUTPUT -> INTERMEDIATE_OUTPUT --- builddefs/xap.mk | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/builddefs/xap.mk b/builddefs/xap.mk index 03c3aad1e1dd..78e138aca0e6 100644 --- a/builddefs/xap.mk +++ b/builddefs/xap.mk @@ -24,21 +24,21 @@ ifneq ("$(wildcard $(USER_NAME)/xap.hjson)","") XAP_FILES += $(USER_NAME)/xap.hjson endif -$(KEYMAP_OUTPUT)/src/config_blob_gz.h: $(INFO_JSON_FILES) +$(INTERMEDIATE_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)) + $(eval CMD=$(QMK_BIN) xap-generate-qmk-blob-h -o "$(INTERMEDIATE_OUTPUT)/src/config_blob_gz.h" -kb $(KEYBOARD) -km $(KEYMAP)) @$(BUILD_CMD) -$(KEYMAP_OUTPUT)/src/xap_generated.inl: $(XAP_FILES) +$(INTERMEDIATE_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)) + $(eval CMD=$(QMK_BIN) xap-generate-qmk-inc -o "$(INTERMEDIATE_OUTPUT)/src/xap_generated.inl" -kb $(KEYBOARD) -km $(KEYMAP)) @$(BUILD_CMD) -$(KEYMAP_OUTPUT)/src/xap_generated.h: $(XAP_FILES) +$(INTERMEDIATE_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)) + $(eval CMD=$(QMK_BIN) xap-generate-qmk-h -o "$(INTERMEDIATE_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 +generated-files: $(INTERMEDIATE_OUTPUT)/src/config_blob_gz.h $(INTERMEDIATE_OUTPUT)/src/xap_generated.inl $(INTERMEDIATE_OUTPUT)/src/xap_generated.h -VPATH += $(KEYMAP_OUTPUT)/src +VPATH += $(INTERMEDIATE_OUTPUT)/src From 3c0280b668d1243863c439b53ae54be887e78ffc Mon Sep 17 00:00:00 2001 From: zvecr Date: Fri, 25 Aug 2023 18:57:33 +0100 Subject: [PATCH 192/203] Fix xap cli bcd output --- lib/chibios-contrib | 2 +- lib/python/xap_client/device.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/chibios-contrib b/lib/chibios-contrib index da78eb3759b8..a224be155ae1 160000 --- a/lib/chibios-contrib +++ b/lib/chibios-contrib @@ -1 +1 @@ -Subproject commit da78eb3759b8d1779b237657c7667baa4aa95ca1 +Subproject commit a224be155ae18d38deccf33a6c1d259b9a5ad8d3 diff --git a/lib/python/xap_client/device.py b/lib/python/xap_client/device.py index 8844f98b1e3d..19936cbfd307 100644 --- a/lib/python/xap_client/device.py +++ b/lib/python/xap_client/device.py @@ -17,7 +17,12 @@ def _u32_to_bcd(val: bytes) -> str: # noqa: N802 """Create BCD string """ - return f'{val>>24}.{val>>16 & 0xFF}.{val & 0xFFFF}' + tmp = "{:08x}".format(val) + major = int(tmp[0:2]) + minor = int(tmp[2:4]) + patch = int(tmp[4:8]) + + return f'{major}.{minor}.{patch}' def _gen_token() -> bytes: From 87fd4b42f5bccc6c53b4183bb4740c9e5ae8ed3d Mon Sep 17 00:00:00 2001 From: zvecr Date: Tue, 29 Aug 2023 02:36:21 +0100 Subject: [PATCH 193/203] Tidy up broadcast in python client --- data/templates/xap/client/python/types.py.j2 | 1 + data/xap/xap_0.1.0.hjson | 2 +- lib/python/xap_client/device.py | 6 +++--- lib/python/xap_client/types.py | 14 ++++++++++++++ 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/data/templates/xap/client/python/types.py.j2 b/data/templates/xap/client/python/types.py.j2 index 76f9614e4e3e..477121981efc 100644 --- a/data/templates/xap/client/python/types.py.j2 +++ b/data/templates/xap/client/python/types.py.j2 @@ -25,6 +25,7 @@ class {{ name }}(namedtuple('{{ name }}', '{{ members }}')): {% set type_definitions = [ {'name':'XAPRequest', 'members': 'token length data', 'fmt':' dict: while not hasattr(event, '_ret'): event.wait(timeout=0.25) - r = XAPResponse.from_bytes(event._ret) - return (r.flags, r.data[:r.length]) + r = XAPBroadcast.from_bytes(event._ret) + return (r.event, r.data[:r.length]) class XAPDevice(XAPDeviceBase): diff --git a/lib/python/xap_client/types.py b/lib/python/xap_client/types.py index ce75cebab9f1..43b80e38a8ab 100644 --- a/lib/python/xap_client/types.py +++ b/lib/python/xap_client/types.py @@ -59,6 +59,20 @@ def to_bytes(self): return self.fmt.pack(*list(self)) +class XAPBroadcast(namedtuple('XAPBroadcast', 'token event length data')): + fmt = Struct(' Date: Tue, 29 Aug 2023 02:37:23 +0100 Subject: [PATCH 194/203] Fix xap docs gen + clarify some broadcast messages --- .../xap/docs/broadcast_messages.md.j2 | 4 +- data/templates/xap/docs/docs.md.j2 | 8 ++-- data/templates/xap/docs/response_flags.md.j2 | 9 +++-- data/templates/xap/docs/routes.md.j2 | 27 ++++++------- .../templates/xap/docs/term_definitions.md.j2 | 8 ++-- data/templates/xap/docs/type_docs.md.j2 | 4 +- data/xap/xap_0.1.0.hjson | 12 +++--- docs/xap_0.0.1.md | 5 +-- docs/xap_0.1.0.md | 22 ++++------- docs/xap_0.2.0.md | 25 ++++-------- docs/xap_0.3.0.md | 38 ++++++++++--------- docs/xap_protocol.md | 2 +- 12 files changed, 78 insertions(+), 86 deletions(-) diff --git a/data/templates/xap/docs/broadcast_messages.md.j2 b/data/templates/xap/docs/broadcast_messages.md.j2 index 9e4ca953f1d6..bb82c98a0ff0 100644 --- a/data/templates/xap/docs/broadcast_messages.md.j2 +++ b/data/templates/xap/docs/broadcast_messages.md.j2 @@ -1,4 +1,4 @@ -{%- for id, message in xap.broadcast_messages.messages | dictsort %} +{% for id, message in xap.broadcast_messages.messages | dictsort %} ### {{ message.name }} - `{{ id }}` {{ message.description }} -{%- endfor %} +{% endfor %} diff --git a/data/templates/xap/docs/docs.md.j2 b/data/templates/xap/docs/docs.md.j2 index a43b50b8ce60..2233cdb3b007 100644 --- a/data/templates/xap/docs/docs.md.j2 +++ b/data/templates/xap/docs/docs.md.j2 @@ -1,9 +1,11 @@ {{ constants.GPL2_HEADER_XML_LIKE }} {{ constants.GENERATED_HEADER_XML_LIKE }} -{%- for item in xap.documentation.order -%} -{%- if not item[0:1] == '!' -%} +{%- for item in xap.documentation.order %} +{% if not item[0:1] == '!' %} {{ xap.documentation.get(item) }} + {% else %} -{%- include item[1:] %} +{% include item[1:] %} + {% endif %} {% endfor %} \ No newline at end of file diff --git a/data/templates/xap/docs/response_flags.md.j2 b/data/templates/xap/docs/response_flags.md.j2 index 852db16fd93c..3914dbb5a359 100644 --- a/data/templates/xap/docs/response_flags.md.j2 +++ b/data/templates/xap/docs/response_flags.md.j2 @@ -1,9 +1,12 @@ |{% 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 %} `{{ bitinfo.define }}` |{% endfor %} + {% for bitnum, bitinfo in xap.response_flags.bits | dictsort | reverse %} -{%- if bitinfo.define != "-" -%} +{% if bitinfo.define != "-" -%} * Bit {{ bitnum }} (`{{ bitinfo.define }}`): {{ bitinfo.description }} {% endif %} -{%- endfor %} \ No newline at end of file +{% endfor %} \ No newline at end of file diff --git a/data/templates/xap/docs/routes.md.j2 b/data/templates/xap/docs/routes.md.j2 index 92afcad20ba7..65a1568f99d8 100644 --- a/data/templates/xap/docs/routes.md.j2 +++ b/data/templates/xap/docs/routes.md.j2 @@ -17,20 +17,20 @@ __{{ name }}:__ `{{ type }}` {% if 'secure' == route.permissions %}__Secure__{% endif %} {%- endmacro -%} -{%- for id, route in xap.routes | dictsort %} +{% 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 %} -{%- if not subroute.routes %} +{% for subid, subroute in route.routes | dictsort %} +{% if not subroute.routes %} | {{ subroute.name }} | `{{ id }} {{ subid }}` | {{ gen_tags(subroute) }} | {{ gen_payloads(subroute) }} | {{ subroute.description | newline_to_br }}| -{%- endif %} -{%- endfor %} +{% endif %} +{% endfor %} -{%- for subid, subroute in route.routes | dictsort %} +{% for subid, subroute in route.routes | dictsort %} {%- if subroute.routes %} #### {{ subroute.name }} - `{{ id }} {{ subid }}` @@ -38,12 +38,13 @@ __{{ name }}:__ `{{ type }}` | Name | Route | Tags | Payloads | Description | | -- | -- | -- | -- | -- | -{%- for subsubid, subsubroute in subroute.routes | dictsort %} -{%- if not subsubroute.routes %} +{% for subsubid, subsubroute in subroute.routes | dictsort %} +{% if not subsubroute.routes %} | {{ subsubroute.name }} | `{{ id }} {{ subid }} {{ subsubid }}` | {{ gen_tags(subsubroute) }} | {{ gen_payloads(subsubroute) }} | {{ subsubroute.description | newline_to_br }}| -{%- endif %} -{%- endfor %} -{%- endif %} -{%- endfor %} {% endif %} -{%- endfor %} +{% endfor %} + +{% endif %} +{% endfor %} +{% endif %} +{% endfor %} diff --git a/data/templates/xap/docs/term_definitions.md.j2 b/data/templates/xap/docs/term_definitions.md.j2 index 9b2863eaa5f4..710a9890e975 100644 --- a/data/templates/xap/docs/term_definitions.md.j2 +++ b/data/templates/xap/docs/term_definitions.md.j2 @@ -1,8 +1,8 @@ | Name | Definition | | -- | -- | -{%- for type, definition in xap.term_definitions | dictsort %} +{% for type, definition in xap.term_definitions | dictsort %} | _{{ type }}_ | {{ definition }} | -{%- endfor %} -{%- for type, definition in xap.type_definitions | dictsort %} +{% 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 %}
`{{ item.type }}` - {{ item.name }}{%- endfor %}{% endif %} | -{%- endfor %} \ No newline at end of file +{% endfor %} \ No newline at end of file diff --git a/data/templates/xap/docs/type_docs.md.j2 b/data/templates/xap/docs/type_docs.md.j2 index 787d94285323..4700681274cb 100644 --- a/data/templates/xap/docs/type_docs.md.j2 +++ b/data/templates/xap/docs/type_docs.md.j2 @@ -1,5 +1,5 @@ | Name | Definition | | -- | -- | -{%- for type, definition in xap.type_docs | dictsort %} +{% for type, definition in xap.type_docs | dictsort %} | _{{ type }}_ | {{ definition }} | -{%- endfor %} \ No newline at end of file +{% endfor %} \ No newline at end of file diff --git a/data/xap/xap_0.1.0.hjson b/data/xap/xap_0.1.0.hjson index 45402c48614c..932d6d09a03a 100755 --- a/data/xap/xap_0.1.0.hjson +++ b/data/xap/xap_0.1.0.hjson @@ -20,7 +20,7 @@ ''' ## 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_. + 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, then the response _payload_ length, and finally the corresponding _payload_. ''' } @@ -100,7 +100,7 @@ ''' 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. + Log message payloads include `u8[Length]` containing the text, where the length of the text is the _broadcast_header.length_ field. **Example Log Broadcast** -- log message "Hello QMK!" @@ -119,10 +119,10 @@ **Example Secure Status Broadcast** -- secure "Unlocking" - | Byte | 0 | 1 | 2 | 3 | - | --- | --- | --- | --- | --- | - | **Purpose** | Token | Token | Broadcast Type | Secure Status | - | **Value** | `0xFF` | `0xFF` | `0x01` | `0x01` | + | Byte | 0 | 1 | 2 | 3 | 4 | + | --- | --- | --- | --- | --- | --- | + | **Purpose** | Token | Token | Broadcast Type | Length | Secure Status | + | **Value** | `0xFF` | `0xFF` | `0x01` | `0x01` | `0x01` | ''' return_type: u8 } diff --git a/docs/xap_0.0.1.md b/docs/xap_0.0.1.md index d94ada81d672..b35acf14790d 100644 --- a/docs/xap_0.0.1.md +++ b/docs/xap_0.0.1.md @@ -1,4 +1,4 @@ - + + + + + + + + + +