summaryrefslogtreecommitdiffstats
path: root/keyboards/keychron/bluetooth
diff options
context:
space:
mode:
Diffstat (limited to 'keyboards/keychron/bluetooth')
-rw-r--r--keyboards/keychron/bluetooth/bat_level_animation.c132
-rw-r--r--keyboards/keychron/bluetooth/bat_level_animation.h23
-rw-r--r--keyboards/keychron/bluetooth/battery.c107
-rw-r--r--keyboards/keychron/bluetooth/battery.h51
-rw-r--r--keyboards/keychron/bluetooth/bluetooth.c441
-rw-r--r--keyboards/keychron/bluetooth/bluetooth.h85
-rw-r--r--keyboards/keychron/bluetooth/bluetooth.mk14
-rw-r--r--keyboards/keychron/bluetooth/bluetooth_config.h32
-rw-r--r--keyboards/keychron/bluetooth/bluetooth_event_type.h44
-rw-r--r--keyboards/keychron/bluetooth/bluetooth_main.c37
-rw-r--r--keyboards/keychron/bluetooth/ckbt51.c591
-rw-r--r--keyboards/keychron/bluetooth/ckbt51.h156
-rw-r--r--keyboards/keychron/bluetooth/factory_test.c308
-rw-r--r--keyboards/keychron/bluetooth/factory_test.h24
-rw-r--r--keyboards/keychron/bluetooth/indicator.c444
-rw-r--r--keyboards/keychron/bluetooth/indicator.h89
-rw-r--r--keyboards/keychron/bluetooth/lpm.c88
-rw-r--r--keyboards/keychron/bluetooth/lpm.h30
-rw-r--r--keyboards/keychron/bluetooth/lpm_stm32l432.c339
-rw-r--r--keyboards/keychron/bluetooth/lpm_stm32l432.h19
-rw-r--r--keyboards/keychron/bluetooth/report_buffer.c141
-rw-r--r--keyboards/keychron/bluetooth/report_buffer.h50
-rw-r--r--keyboards/keychron/bluetooth/transport.c182
-rw-r--r--keyboards/keychron/bluetooth/transport.h32
24 files changed, 3459 insertions, 0 deletions
diff --git a/keyboards/keychron/bluetooth/bat_level_animation.c b/keyboards/keychron/bluetooth/bat_level_animation.c
new file mode 100644
index 0000000000..83a4f91cae
--- /dev/null
+++ b/keyboards/keychron/bluetooth/bat_level_animation.c
@@ -0,0 +1,132 @@
+
+#include "quantum.h"
+#include "bluetooth.h"
+#include "indicator.h"
+#include "lpm.h"
+#if defined(PROTOCOL_CHIBIOS)
+# include <usb_main.h>
+#elif if defined(PROTOCOL_LUFA)
+# include "lufa.h"
+#endif
+
+#ifndef BAT_LEVEL_GROWING_INTERVAL
+# define BAT_LEVEL_GROWING_INTERVAL 150
+#endif
+
+#ifndef BAT_LEVEL_ON_INTERVAL
+# define BAT_LEVEL_ON_INTERVAL 3000
+#endif
+
+enum {
+ BAT_LVL_ANI_NONE,
+ BAT_LVL_ANI_GROWING,
+ BAT_LVL_ANI_BLINK_OFF,
+ BAT_LVL_ANI_BLINK_ON,
+};
+
+static uint8_t animation_state = 0;
+static uint32_t bat_lvl_ani_timer_buffer = 0;
+static uint8_t bat_percentage;
+static uint8_t cur_percentage;
+static uint32_t time_interval;
+#ifdef RGB_MATRIX_ENABLE
+static uint8_t r, g, b;
+#endif
+
+extern indicator_config_t indicator_config;
+extern backlight_state_t original_backlight_state;
+
+void bat_level_animiation_start(uint8_t percentage) {
+ /* Turn on backlight mode for indicator */
+ indicator_enable();
+
+ animation_state = BAT_LVL_ANI_GROWING;
+ bat_percentage = percentage;
+ bat_lvl_ani_timer_buffer = sync_timer_read32();
+ cur_percentage = 0;
+ time_interval = BAT_LEVEL_GROWING_INTERVAL;
+#ifdef RGB_MATRIX_ENABLE
+ r = g = b = 255;
+#endif
+}
+
+void bat_level_animiation_stop(void) {
+ animation_state = BAT_LVL_ANI_NONE;
+}
+
+bool bat_level_animiation_actived(void) {
+ return animation_state;
+}
+
+void bat_level_animiation_indicate(void) {
+#ifdef LED_MATRIX_ENABLE
+ uint8_t bat_lvl_led_list[10] = BAT_LEVEL_LED_LIST;
+
+ for (uint8_t i = 0; i <= DRIVER_LED_TOTAL; i++) {
+ led_matrix_set_value(i, 0);
+ }
+
+ if (animation_state == BAT_LVL_ANI_GROWING || animation_state == BAT_LVL_ANI_BLINK_ON)
+ for (uint8_t i = 0; i < cur_percentage / 10; i++)
+ led_matrix_set_value(bat_lvl_led_list[i], 255);
+#endif
+
+#ifdef RGB_MATRIX_ENABLE
+ uint8_t bat_lvl_led_list[10] = BAT_LEVEL_LED_LIST;
+
+ for (uint8_t i = 0; i <= DRIVER_LED_TOTAL; i++) {
+ rgb_matrix_set_color(i, 0, 0, 0);
+ }
+
+ if (animation_state == BAT_LVL_ANI_GROWING || animation_state == BAT_LVL_ANI_BLINK_ON) {
+ for (uint8_t i = 0; i < cur_percentage / 10; i++) {
+ rgb_matrix_set_color(bat_lvl_led_list[i], r, g, b);
+ }
+ }
+#endif
+}
+
+void bat_level_animiation_update(void) {
+ switch (animation_state) {
+ case BAT_LVL_ANI_GROWING:
+ if (cur_percentage < bat_percentage)
+ cur_percentage += 10;
+ else {
+ if (cur_percentage == 0) cur_percentage = 10;
+ animation_state = BAT_LVL_ANI_BLINK_OFF;
+ }
+ break;
+
+ case BAT_LVL_ANI_BLINK_OFF:
+#ifdef RGB_MATRIX_ENABLE
+ if (bat_percentage < 30) {
+ r = 255;
+ b = g = 0;
+ } else {
+ r = b = 0;
+ g = 255;
+ }
+#endif
+ time_interval = BAT_LEVEL_ON_INTERVAL;
+ animation_state = BAT_LVL_ANI_BLINK_ON;
+ break;
+
+ case BAT_LVL_ANI_BLINK_ON:
+ animation_state = BAT_LVL_ANI_NONE;
+ if (indicator_config.value == 0 && !indicator_is_backlit_enabled_eeprom()) {
+ indicator_disable();
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ bat_lvl_ani_timer_buffer = sync_timer_read32();
+}
+
+void bat_level_animiation_task(void) {
+ if (animation_state && sync_timer_elapsed32(bat_lvl_ani_timer_buffer) > time_interval) {
+ bat_level_animiation_update();
+ }
+}
diff --git a/keyboards/keychron/bluetooth/bat_level_animation.h b/keyboards/keychron/bluetooth/bat_level_animation.h
new file mode 100644
index 0000000000..716e924103
--- /dev/null
+++ b/keyboards/keychron/bluetooth/bat_level_animation.h
@@ -0,0 +1,23 @@
+/* Copyright 2022 @ lokher (https://www.keychron.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+void bat_level_animiation_start(uint8_t percentage);
+void bat_level_animiation_stop(void);
+bool bat_level_animiation_actived(void);
+void bat_level_animiation_indicate(void);
+void bat_level_animiation_task(void);
diff --git a/keyboards/keychron/bluetooth/battery.c b/keyboards/keychron/bluetooth/battery.c
new file mode 100644
index 0000000000..6dd453357e
--- /dev/null
+++ b/keyboards/keychron/bluetooth/battery.c
@@ -0,0 +1,107 @@
+/* Copyright 2022 @ lokher (https://www.keychron.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "quantum.h"
+#include "bluetooth.h"
+#include "battery.h"
+#include "transport.h"
+#include "ckbt51.h"
+#include "lpm.h"
+
+#define BATTERY_EMPTY_COUNT 10
+#define CRITICAL_LOW_COUNT 20
+
+static uint32_t bat_monitor_timer_buffer = 0;
+static uint16_t voltage = FULL_VOLTAGE_VALUE;
+static uint8_t bat_empty = 0;
+static uint8_t critical_low = 0;
+static uint8_t bat_state;
+
+void battery_init(void) {
+ bat_monitor_timer_buffer = 0;
+ bat_state = BAT_NOT_CHARGING;
+}
+__attribute__((weak)) void battery_measure(void) {}
+
+/* Calculate the voltage */
+__attribute__((weak)) void battery_calculate_voltage(uint16_t value) {}
+
+void battery_set_voltage(uint16_t value) {
+ voltage = value;
+}
+
+uint16_t battery_get_voltage(void) {
+ return voltage;
+}
+
+uint8_t battery_get_percentage(void) {
+ if (voltage > FULL_VOLTAGE_VALUE) return 100;
+
+ if (voltage > EMPTY_VOLTAGE_VALUE) {
+ return ((uint32_t)voltage - EMPTY_VOLTAGE_VALUE) * 80 / (FULL_VOLTAGE_VALUE - EMPTY_VOLTAGE_VALUE) + 20;
+ }
+
+ if (voltage > SHUTDOWN_VOLTAGE_VALUE) {
+ return ((uint32_t)voltage - SHUTDOWN_VOLTAGE_VALUE) * 20 / (EMPTY_VOLTAGE_VALUE - SHUTDOWN_VOLTAGE_VALUE);
+ } else
+ return 0;
+}
+
+bool battery_is_empty(void) {
+ return bat_empty > BATTERY_EMPTY_COUNT;
+}
+
+bool battery_is_critical_low(void) {
+ return critical_low > CRITICAL_LOW_COUNT;
+}
+
+void battery_check_empty(void) {
+ if (voltage < EMPTY_VOLTAGE_VALUE) {
+ if (bat_empty <= BATTERY_EMPTY_COUNT) {
+ if (++bat_empty > BATTERY_EMPTY_COUNT) indicator_battery_low_enable(true);
+ }
+ } else if (bat_empty <= BATTERY_EMPTY_COUNT) {
+ bat_empty = BATTERY_EMPTY_COUNT;
+ }
+}
+
+void battery_check_critical_low(void) {
+ if (voltage < SHUTDOWN_VOLTAGE_VALUE) {
+ if (critical_low <= CRITICAL_LOW_COUNT) {
+ if (++critical_low > CRITICAL_LOW_COUNT) bluetooth_low_battery_shutdown();
+ }
+ } else if (critical_low <= CRITICAL_LOW_COUNT) {
+ critical_low = 0;
+ }
+}
+
+void battery_task(void) {
+ if (get_transport() == TRANSPORT_BLUETOOTH && bluetooth_get_state() == BLUETOOTH_CONNECTED) {
+ if (sync_timer_elapsed32(bat_monitor_timer_buffer) > VOLTAGE_MEASURE_INTERVAL) {
+ battery_check_empty();
+ battery_check_critical_low();
+
+ bat_monitor_timer_buffer = sync_timer_read32();
+ battery_measure();
+ }
+ }
+
+ if ((bat_empty || critical_low) && usb_power_connected()) {
+ bat_empty = false;
+ critical_low = false;
+ indicator_battery_low_enable(false);
+ }
+}
diff --git a/keyboards/keychron/bluetooth/battery.h b/keyboards/keychron/bluetooth/battery.h
new file mode 100644
index 0000000000..5d00a529c6
--- /dev/null
+++ b/keyboards/keychron/bluetooth/battery.h
@@ -0,0 +1,51 @@
+/* Copyright 2022 @ lokher (https://www.keychron.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+enum {
+ BAT_NOT_CHARGING = 0,
+ BAT_CHARGING,
+ BAT_CHARGING_FINISHED,
+};
+
+#ifndef FULL_VOLTAGE_VALUE
+# define FULL_VOLTAGE_VALUE 4100
+#endif
+
+#ifndef EMPTY_VOLTAGE_VALUE
+# define EMPTY_VOLTAGE_VALUE 3500
+#endif
+
+#ifndef SHUTDOWN_VOLTAGE_VALUE
+# define SHUTDOWN_VOLTAGE_VALUE 3300
+#endif
+
+#ifndef VOLTAGE_MEASURE_INTERVAL
+# define VOLTAGE_MEASURE_INTERVAL 3000
+#endif
+
+void battery_init(void);
+void battery_measure(void);
+void battery_calculte_voltage(uint16_t value);
+void battery_set_voltage(uint16_t value);
+uint16_t battery_get_voltage(void);
+uint8_t battery_get_percentage(void);
+void indicator_battery_low_enable(bool enable);
+bool battery_is_empty(void);
+bool battery_is_critical_low(void);
+
+void battery_task(void);
diff --git a/keyboards/keychron/bluetooth/bluetooth.c b/keyboards/keychron/bluetooth/bluetooth.c
new file mode 100644
index 0000000000..10c79257a9
--- /dev/null
+++ b/keyboards/keychron/bluetooth/bluetooth.c
@@ -0,0 +1,441 @@
+/* Copyright 2022 @ lokher (https://www.keychron.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "quantum.h"
+#include "bluetooth.h"
+#include "report_buffer.h"
+#include "lpm.h"
+#include "battery.h"
+#include "indicator.h"
+#include "transport.h"
+
+extern uint8_t pairing_indication;
+extern host_driver_t chibios_driver;
+extern report_buffer_t kb_rpt;
+extern uint32_t retry_time_buffer;
+extern uint8_t retry;
+
+#ifdef NKRO_ENABLE
+typedef struct {
+ bool usb : 1;
+ bool bluetooth : 1;
+} nkro_t;
+
+extern nkro_t nkro;
+#endif
+
+static uint8_t host_index = 0;
+static uint8_t led_state = 0;
+
+extern bluetooth_transport_t bluetooth_transport;
+static bluetooth_state_t bt_state = BLUETOOTH_RESET;
+static bool pincodeEntry = false;
+uint8_t bluetooth_report_protocol = true;
+
+/* declarations */
+uint8_t bluetooth_keyboard_leds(void);
+void bluetooth_send_keyboard(report_keyboard_t *report);
+void bluetooth_send_mouse(report_mouse_t *report);
+void bluetooth_send_system(uint16_t data);
+void bluetooth_send_consumer(uint16_t data);
+/* host struct */
+host_driver_t bluetooth_driver = {bluetooth_keyboard_leds, bluetooth_send_keyboard, bluetooth_send_mouse, bluetooth_send_system, bluetooth_send_consumer};
+
+#define BLUETOOTH_EVENT_QUEUE_SIZE 16
+bluetooth_event_t bt_event_queue[BLUETOOTH_EVENT_QUEUE_SIZE];
+uint8_t bt_event_queue_head;
+uint8_t bt_event_queue_tail;
+
+void bluetooth_bt_event_queue_init(void) {
+ // Initialise the event queue
+ memset(&bt_event_queue, 0, sizeof(bt_event_queue));
+ bt_event_queue_head = 0;
+ bt_event_queue_tail = 0;
+}
+
+bool bluetooth_event_queue_enqueue(bluetooth_event_t event) {
+ uint8_t next = (bt_event_queue_head + 1) % BLUETOOTH_EVENT_QUEUE_SIZE;
+ if (next == bt_event_queue_tail) {
+ /* Override the first report */
+ bt_event_queue_tail = (bt_event_queue_tail + 1) % BLUETOOTH_EVENT_QUEUE_SIZE;
+ }
+ bt_event_queue[bt_event_queue_head] = event;
+ bt_event_queue_head = next;
+ return true;
+}
+
+static inline bool bluetooth_event_queue_dequeue(bluetooth_event_t *event) {
+ if (bt_event_queue_head == bt_event_queue_tail) {
+ return false;
+ }
+ *event = bt_event_queue[bt_event_queue_tail];
+ bt_event_queue_tail = (bt_event_queue_tail + 1) % BLUETOOTH_EVENT_QUEUE_SIZE;
+ return true;
+}
+
+/*
+ * Bluetooth init.
+ */
+void bluetooth_init(void) {
+ bt_state = BLUETOOTH_INITIALIZED;
+
+ bluetooth_bt_event_queue_init();
+#ifndef DISABLE_REPORT_BUFFER
+ report_buffer_init();
+#endif
+ indicator_init();
+#ifdef BLUETOOTH_INT_INPUT_PIN
+ setPinInputHigh(BLUETOOTH_INT_INPUT_PIN);
+#endif
+
+ lpm_init();
+}
+
+/*
+ * Bluetooth trasponrt init. Bluetooth module driver shall use this function to register a callback
+ * to its implementation.
+ */
+void bluetooth_set_transport(bluetooth_transport_t *transport) {
+ if (transport) memcpy(&bluetooth_transport, transport, sizeof(bluetooth_transport_t));
+}
+
+/*
+ * Enter pairing with current host index
+ */
+void bluetooth_pairing(void) {
+ if (battery_is_critical_low()) return;
+
+ bluetooth_pairing_ex(0, NULL);
+ bt_state = BLUETOOTH_PARING;
+}
+
+/*
+ * Enter pairing with specified host index and param
+ */
+void bluetooth_pairing_ex(uint8_t host_idx, void *param) {
+ if (battery_is_critical_low()) return;
+
+ if (bluetooth_transport.pairing_ex) bluetooth_transport.pairing_ex(host_idx, param);
+ bt_state = BLUETOOTH_PARING;
+
+ host_index = host_idx;
+}
+
+/*
+ * Initiate connection request to paired host
+ */
+void bluetooth_connect(void) {
+ /* Work around empty report after wakeup, which leads to reconneect/disconnected loop */
+ if (battery_is_critical_low() || sync_timer_read32() == 0) return;
+
+ bluetooth_transport.connect_ex(0, 0);
+ bt_state = BLUETOOTH_RECONNECTING;
+}
+
+/*
+ * Initiate connection request to paired host with argument
+ */
+void bluetooth_connect_ex(uint8_t host_idx, uint16_t timeout) {
+ if (battery_is_critical_low()) return;
+
+ if (host_idx != 0) {
+ if (host_index == host_idx && bt_state == BLUETOOTH_CONNECTED) return;
+ host_index = host_idx;
+ led_state = 0;
+ }
+ bluetooth_transport.connect_ex(host_idx, timeout);
+ bt_state = BLUETOOTH_RECONNECTING;
+}
+
+/* Initiate a disconnection */
+void bluetooth_disconnect(void) {
+ if (bluetooth_transport.disconnect) bluetooth_transport.disconnect();
+}
+
+
+/* Called when the BT device is reset. */
+static void bluetooth_enter_reset(uint8_t reason) {
+ bt_state = BLUETOOTH_RESET;
+ bluetooth_enter_reset_kb(reason);
+}
+
+/* Enters discoverable state. Upon entering this state we perform the following actions:
+ * - change state to BLUETOOTH_PARING
+ * - set pairing indication
+ */
+static void bluetooth_enter_discoverable(uint8_t host_idx) {
+ bt_state = BLUETOOTH_PARING;
+ indicator_set(bt_state, host_idx);
+ bluetooth_enter_discoverable_kb(host_idx);
+}
+
+/*
+ * Enters reconnecting state. Upon entering this state we perform the following actions:
+ * - change state to RECONNECTING
+ * - set reconnect indication
+ */
+static void bluetooth_enter_reconnecting(uint8_t host_idx) {
+ bt_state = BLUETOOTH_RECONNECTING;
+ indicator_set(bt_state, host_idx);
+ bluetooth_enter_reconnecting_kb(host_idx);
+}
+
+/* Enters connected state. Upon entering this state we perform the following actions:
+ * - change state to CONNECTED
+ * - set connected indication
+ * - enable bluetooth NKRO is support
+ */
+static void bluetooth_enter_connected(uint8_t host_idx) {
+ bt_state = BLUETOOTH_CONNECTED;
+ indicator_set(bt_state, host_idx);
+ host_index = host_idx;
+
+ clear_keyboard();
+
+ /* Enable NKRO since it may be disabled in pin code entry */
+#if defined(NKRO_ENABLE) && defined(BLUETOOTH_NKRO_ENABLE)
+ keymap_config.nkro = nkro.bluetooth;
+#else
+ keymap_config.nkro = false;
+#endif
+
+ bluetooth_enter_connected_kb(host_idx);
+
+ if (battery_is_empty()) indicator_battery_low_enable(true);
+}
+
+/* Enters disconnected state. Upon entering this state we perform the following actions:
+ * - change state to DISCONNECTED
+ * - set disconnected indication
+ */
+static void bluetooth_enter_disconnected(uint8_t host_idx) {
+ uint8_t previous_state = bt_state;
+ bt_state = BLUETOOTH_DISCONNECTED;
+
+ if (previous_state == BLUETOOTH_CONNECTED) {
+ lpm_timer_reset();
+ indicator_set(BLUETOOTH_SUSPEND, host_idx);
+ } else
+ indicator_set(bt_state, host_idx);
+
+#ifndef DISABLE_REPORT_BUFFER
+ report_buffer_init();
+#endif
+ retry = 0;
+ bluetooth_enter_disconnected_kb(host_idx);
+ indicator_battery_low_enable(false);
+}
+
+
+/* Enter pin code entry state. */
+static void bluetooth_enter_pin_code_entry(void) {
+#if defined(NKRO_ENABLE)
+ keymap_config.nkro = FALSE;
+#endif
+ pincodeEntry = true;
+ bluetooth_enter_pin_code_entry_kb();
+}
+
+/* Exit pin code entry state. */
+static void bluetooth_exit_pin_code_entry(void) {
+#if defined(NKRO_ENABLE)
+ keymap_config.nkro = true;
+#endif
+ pincodeEntry = false;
+ bluetooth_exit_pin_code_entry_kb();
+}
+
+__attribute__((weak)) void bluetooth_enter_reset_kb(uint8_t reason){};
+__attribute__((weak)) void bluetooth_enter_discoverable_kb(uint8_t host_idx){};
+__attribute__((weak)) void bluetooth_enter_reconnecting_kb(uint8_t host_idx){};
+__attribute__((weak)) void bluetooth_enter_connected_kb(uint8_t host_idx){};
+__attribute__((weak)) void bluetooth_enter_disconnected_kb(uint8_t host_idx){};
+__attribute__((weak)) void bluetooth_enter_pin_code_entry_kb(void) {}
+__attribute__((weak)) void bluetooth_exit_pin_code_entry_kb(void){};
+
+/* */
+static void bluetooth_hid_set_protocol(bool report_protocol) {
+ bluetooth_report_protocol = false;
+}
+
+uint8_t bluetooth_keyboard_leds(void) {
+ if (bt_state == BLUETOOTH_CONNECTED) {
+ return led_state;
+ }
+
+ return 0;
+}
+
+extern keymap_config_t keymap_config;
+
+void bluetooth_send_keyboard(report_keyboard_t *report) {
+ if (bt_state == BLUETOOTH_PARING && !pincodeEntry) return;
+
+ if (bt_state == BLUETOOTH_CONNECTED || (bt_state == BLUETOOTH_PARING && pincodeEntry)) {
+# if defined(NKRO_ENABLE)
+ if (bluetooth_report_protocol && keymap_config.nkro) {
+ if (bluetooth_transport.send_nkro) {
+# ifndef DISABLE_REPORT_BUFFER
+ bool firstBuffer = false;
+ if (report_buffer_is_empty() && report_buffer_next_inverval() && report_buffer_get_retry() == 0) {
+ firstBuffer = true;
+ }
+
+ report_buffer_t report_buffer;
+ report_buffer.type = REPORT_TYPE_NKRO;
+ memcpy(&report_buffer.keyboard, report, sizeof(report_keyboard_t));
+ report_buffer_enqueue(&report_buffer);
+
+ if (firstBuffer) {
+ report_buffer_set_retry(0);
+ report_buffer_task();
+ }
+# else
+ bluetooth_transport.send_nkro(&report->nkro.mods);
+# endif
+ }
+ } else
+# endif
+ {
+ //#ifdef KEYBOARD_SHARED_EP
+ if (bluetooth_transport.send_keyboard) {
+# ifndef DISABLE_REPORT_BUFFER
+ if (report_buffer_is_empty() && report_buffer_next_inverval()) {
+ bluetooth_transport.send_keyboard(&report->mods);
+ report_buffer_update_timer();
+ } else {
+ report_buffer_t report_buffer;
+ report_buffer.type = REPORT_TYPE_KB;
+ memcpy(&report_buffer.keyboard, report, sizeof(report_keyboard_t));
+ report_buffer_enqueue(&report_buffer);
+ }
+# else
+ bluetooth_transport.send_keyboard(&report->mods);
+# endif
+ }
+ //#endif
+ }
+
+ } else if (bt_state != BLUETOOTH_RESET) {
+ bluetooth_connect();
+ }
+}
+
+void bluetooth_send_mouse(report_mouse_t *report) {
+ if (bt_state == BLUETOOTH_CONNECTED) {
+ if (bluetooth_transport.send_mouse) bluetooth_transport.send_mouse((uint8_t *)report);
+ } else if (bt_state != BLUETOOTH_RESET) {
+ bluetooth_connect();
+ }
+}
+
+void bluetooth_send_system(uint16_t data) {
+ if (bt_state == BLUETOOTH_CONNECTED) {
+ if (bluetooth_transport.send_system) bluetooth_transport.send_system(data);
+ } else if (bt_state != BLUETOOTH_RESET) {
+ bluetooth_connect();
+ }
+}
+
+void bluetooth_send_consumer(uint16_t data) {
+ if (bt_state == BLUETOOTH_CONNECTED) {
+#ifndef DISABLE_REPORT_BUFFER
+ if (report_buffer_is_empty() && report_buffer_next_inverval()) {
+ if (bluetooth_transport.send_consumer) bluetooth_transport.send_consumer(data);
+ report_buffer_update_timer();
+ } else {
+ report_buffer_t report_buffer;
+ report_buffer.type = REPORT_TYPE_CONSUMER;
+ report_buffer.consumer = data;
+ report_buffer_enqueue(&report_buffer);
+ }
+#else
+ if (bluetooth_transport.send_consumer) bluetooth_transport.send_consumer(data);
+#endif
+ } else if (bt_state != BLUETOOTH_RESET) {
+ bluetooth_connect();
+ }
+}
+
+void bluetooth_low_battery_shutdown(void) {
+ indicator_battery_low_enable(false);
+ bluetooth_disconnect();
+}
+
+void bluetooth_event_queue_task(void) {
+ bluetooth_event_t event;
+ while (bluetooth_event_queue_dequeue(&event)) {
+ switch (event.evt_type) {
+ case EVT_RESET:
+ bluetooth_enter_reset(event.params.reason);
+ break;
+ case EVT_CONNECTED:
+ bluetooth_enter_connected(event.params.hostIndex);
+ break;
+ case EVT_DISCOVERABLE:
+ bluetooth_enter_discoverable(event.params.hostIndex);
+ break;
+ case EVT_RECONNECTING:
+ bluetooth_enter_reconnecting(event.params.hostIndex);
+ break;
+ case EVT_DISCONNECTED:
+ led_state = 0;
+ bluetooth_enter_disconnected(event.params.hostIndex);
+ break;
+ case EVT_PINCODE_ENTRY:
+ bluetooth_enter_pin_code_entry();
+ break;
+ case EVT_EXIT_PINCODE_ENTRY:
+ bluetooth_exit_pin_code_entry();
+ break;
+ case EVT_HID_INDICATOR:
+ led_state = event.params.led;
+ break;
+ case EVT_HID_SET_PROTOCOL:
+ bluetooth_hid_set_protocol(event.params.protocol);
+ break;
+ case EVT_CONECTION_INTERVAL:
+ report_buffer_set_inverval(event.params.interval);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void bluetooth_task(void) {
+
+ bluetooth_transport.task();
+ bluetooth_event_queue_task();
+#ifndef DISABLE_REPORT_BUFFER
+ report_buffer_task();
+#endif
+ indicator_task();
+ battery_task();
+ lpm_task();
+}
+
+bluetooth_state_t bluetooth_get_state(void) {
+ return bt_state;
+};
+
+bool process_record_kb(uint16_t keycode, keyrecord_t *record) {
+ if (get_transport() == TRANSPORT_BLUETOOTH) {
+ lpm_timer_reset();
+ }
+
+ return process_record_user(keycode, record);
+}
diff --git a/keyboards/keychron/bluetooth/bluetooth.h b/keyboards/keychron/bluetooth/bluetooth.h
new file mode 100644
index 0000000000..b24df48845
--- /dev/null
+++ b/keyboards/keychron/bluetooth/bluetooth.h
@@ -0,0 +1,85 @@
+/* Copyright 2022 @ lokher (https://www.keychron.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "bluetooth_event_type.h"
+
+/* Low power mode */
+#ifndef LOW_POWER_MODE
+# define LOW_POWER_MODE PM_STOP1
+#endif
+
+/* Wake pin used for blueooth module/controller to wake up MCU in low power mode*/
+#ifndef BLUETOOTH_INT_INPUT_PIN
+# define WAKE_PIN A5
+#endif
+
+/* Type of an enumeration of the possible BT state.*/
+typedef enum {
+ BLUETOOTH_RESET,
+ BLUETOOTH_INITIALIZED, // 1
+ BLUETOOTH_DISCONNECTED, // 2
+ BLUETOOTH_CONNECTED, // 3
+ BLUETOOTH_PARING, // 4
+ BLUETOOTH_RECONNECTING, // 5
+ BLUETOOTH_SUSPEND
+} bluetooth_state_t;
+
+extern event_listener_t bt_driver;
+
+typedef struct {
+ void (*init)(bool);
+ void (*connect_ex)(uint8_t, uint16_t);
+ void (*pairing_ex)(uint8_t, void *);
+ void (*disconnect)(void);
+ void (*send_keyboard)(uint8_t *);
+ void (*send_nkro)(uint8_t *);
+ void (*send_consumer)(uint16_t);
+ void (*send_system)(uint16_t);
+ void (*send_mouse)(uint8_t *);
+ void (*task)(void);
+} bluetooth_transport_t;
+
+void bluetooth_init(void);
+void bluetooth_set_transport(bluetooth_transport_t *transport);
+void bluetooth_task(void);
+
+bool bluetooth_event_queue_enqueue(bluetooth_event_t event);
+
+void bluetooth_connect(void);
+void bluetooth_connect_ex(uint8_t host_idx, uint16_t timeout);
+void bluetooth_disconnect(void);
+
+void bluetooth_pairing(void);
+void bluetooth_pairing_ex(uint8_t host_idx, void *param);
+bool bluetooth_is_activated(void);
+
+void bluetooth_enter_reset_kb(uint8_t reason);
+void bluetooth_enter_discoverable_kb(uint8_t host_idx);
+void bluetooth_enter_reconnecting_kb(uint8_t host_idx);
+void bluetooth_enter_connected_kb(uint8_t host_idx);
+void bluetooth_enter_disconnected_kb(uint8_t host_idx);
+void bluetooth_enter_pin_code_entry_kb(void);
+void bluetooth_exit_pin_code_entry_kb(void);
+
+void bluetooth_task(void);
+void bluetooth_pre_task(void);
+void bluetooth_post_task(void);
+bluetooth_state_t bluetooth_get_state(void);