Added wireless.

This commit is contained in:
Su 2024-07-29 18:22:03 +08:00
parent d538451adb
commit ec5a9767d2
14 changed files with 1803 additions and 0 deletions

View File

@ -0,0 +1,304 @@
// Copyright 2024 Su (@isuua)
// SPDX-License-Identifier: GPL-2.0-or-later
#include "quantum.h"
#include "lowpower.h"
#ifndef LPWR_TIMEOUT
# define LPWR_TIMEOUT 300000 // 5min
#endif
#ifndef LPWR_PRESLEEP_DELAY
# define LPWR_PRESLEEP_DELAY 200
#endif
#ifndef LPWR_STOP_DELAY
# define LPWR_STOP_DELAY 200
#endif
#ifndef LPWR_WAKEUP_DELAY
# define LPWR_WAKEUP_DELAY 200
#endif
static lpwr_state_t lpwr_state = LPWR_NORMAL;
static lpwr_mode_t lpwr_mode = LPWR_MODE_TIMEOUT;
static uint32_t lpwr_timeout_value = LPWR_TIMEOUT;
static uint32_t lpwr_timestamp = 0x00;
static lpwr_wakeupcd_t lpwr_wakeupcd = LPWR_WAKEUP_NONE;
static bool manual_timeout = false;
static bool rgb_enable_bak = false;
void last_matrix_activity_trigger(void);
void lpwr_clock_enable(void);
void lpwr_enter_stop(void);
void lpwr_exti_init(void);
void mcu_stop_mode(void);
extern void matrix_init_pins(void);
lpwr_state_t lpwr_get_state(void) {
return lpwr_state;
}
void lpwr_set_state(lpwr_state_t state) {
lpwr_state = state;
}
lpwr_mode_t lpwr_get_mode(void) {
return lpwr_mode;
}
void lpwr_set_mode(lpwr_mode_t mode) {
lpwr_mode = mode;
}
void lpwr_set_timeout_value(uint32_t timeout) {
lpwr_timeout_value = timeout;
}
uint32_t lpwr_timeout_value_read(void) {
return lpwr_timeout_value;
}
void lpwr_update_timestamp(void) {
lpwr_timestamp = sync_timer_read32();
}
uint32_t lpwr_timestamp_read(void) {
return lpwr_timestamp;
}
void lpwr_set_sleep_wakeupcd(lpwr_wakeupcd_t wakeupcd) {
lpwr_wakeupcd = wakeupcd;
}
lpwr_wakeupcd_t lpwr_get_sleep_wakeupcd(void) {
return lpwr_wakeupcd;
}
void lpwr_clock_enable(void) __attribute__((weak));
void lpwr_clock_enable(void) {}
void lpwr_exti_init(void) __attribute__((weak));
void lpwr_exti_init(void) {}
void mcu_stop_mode(void) __attribute__((weak));
void mcu_stop_mode(void) {}
void lpwr_enter_stop(void) {
chSysLock();
lpwr_exti_init();
chSysUnlock();
chSysDisable();
mcu_stop_mode();
lpwr_clock_enable();
matrix_init_pins();
chSysEnable();
}
void lpwr_set_timeout_manual(bool enable) {
manual_timeout = enable;
}
bool lpwr_get_timeout_manual(void) {
return manual_timeout;
}
// 2.4g mode, host state
void md_receive_host_cb(bool resume) {
if (resume) {
if (lpwr_get_state() != LPWR_NORMAL) {
lpwr_update_timestamp();
lpwr_set_state(LPWR_WAKEUP);
}
} else {
if (lpwr_get_state() == LPWR_NORMAL) {
manual_timeout = true;
}
}
}
bool lpwr_is_allow_timeout_hook(void) __attribute__((weak));
bool lpwr_is_allow_timeout_hook(void) {
return true;
}
bool lpwr_is_allow_timeout(void) __attribute__((weak));
bool lpwr_is_allow_timeout(void) {
uint32_t timeout = lpwr_timeout_value_read();
if (lpwr_is_allow_timeout_hook() != true) {
return false;
}
if (manual_timeout || (timeout && (last_input_activity_elapsed() >= timeout))) {
manual_timeout = false;
return true;
}
return false;
}
bool lpwr_is_allow_presleep_hook(void) __attribute__((weak));
bool lpwr_is_allow_presleep_hook(void) {
return true;
}
bool lpwr_is_allow_presleep(void) __attribute__((weak));
bool lpwr_is_allow_presleep(void) {
uint32_t delay = LPWR_PRESLEEP_DELAY;
if (lpwr_is_allow_presleep_hook() != true) {
return false;
}
if (!delay || (sync_timer_elapsed32(lpwr_timestamp_read()) >= delay)) {
return true;
}
return false;
}
bool lpwr_is_allow_stop_hook(void) __attribute__((weak));
bool lpwr_is_allow_stop_hook(void) {
return true;
}
bool lpwr_is_allow_stop(void) __attribute__((weak));
bool lpwr_is_allow_stop(void) {
uint32_t delay = LPWR_STOP_DELAY;
if (lpwr_is_allow_stop_hook() != true) {
return false;
}
if (!delay || (sync_timer_elapsed32(lpwr_timestamp_read()) >= delay)) {
return true;
}
return false;
}
bool lpwr_is_allow_wakeup_hook(void) __attribute__((weak));
bool lpwr_is_allow_wakeup_hook(void) {
return true;
}
bool lpwr_is_allow_wakeup(void) __attribute__((weak));
bool lpwr_is_allow_wakeup(void) {
uint32_t delay = LPWR_WAKEUP_DELAY;
if (lpwr_is_allow_wakeup_hook() != true) {
return false;
}
if (!delay || (sync_timer_elapsed32(lpwr_timestamp_read()) >= delay)) {
return true;
}
return false;
}
void lpwr_presleep_hook(void) __attribute__((weak));
void lpwr_presleep_hook(void) {}
void lpwr_presleep_cb(void) __attribute__((weak));
void lpwr_presleep_cb(void) {
#if defined(RGB_MATRIX_ENABLE)
rgb_enable_bak = rgb_matrix_is_enabled();
rgb_matrix_disable_noeeprom();
#elif defined(RGBLIGHT_ENABLE)
rgb_enable_bak = rgblight_is_enabled();
rgblight_disable_noeeprom();
#else
rgb_enable_bak = false;
#endif
suspend_power_down();
lpwr_presleep_hook();
}
void lpwr_stop_hook_pre(void) __attribute__((weak));
void lpwr_stop_hook_pre(void) {}
void lpwr_stop_hook_post(void) __attribute__((weak));
void lpwr_stop_hook_post(void) {}
void lpwr_stop_cb(void) __attribute__((weak));
void lpwr_stop_cb(void) {
lpwr_set_sleep_wakeupcd(LPWR_WAKEUP_NONE);
lpwr_stop_hook_pre();
lpwr_enter_stop();
switch (lpwr_get_sleep_wakeupcd()) {
case LPWR_WAKEUP_UART: {
lpwr_set_state(LPWR_STOP);
} break;
default: {
lpwr_set_state(LPWR_WAKEUP);
} break;
}
lpwr_stop_hook_post();
}
void lpwr_wakeup_hook(void) __attribute__((weak));
void lpwr_wakeup_hook(void) {}
void lpwr_wakeup_cb(void) __attribute__((weak));
void lpwr_wakeup_cb(void) {
if (rgb_enable_bak) {
#if defined(RGB_MATRIX_ENABLE)
rgb_matrix_enable_noeeprom();
#elif defined(RGBLIGHT_ENABLE)
rgblight_enable_noeeprom();
#endif
}
suspend_wakeup_init();
lpwr_wakeup_hook();
last_matrix_activity_trigger();
}
void lpwr_task(void) __attribute__((weak));
void lpwr_task(void) {
switch (lpwr_get_state()) {
case LPWR_NORMAL: {
if (lpwr_is_allow_timeout()) {
lpwr_update_timestamp();
lpwr_set_state(LPWR_PRESLEEP);
}
} break;
case LPWR_PRESLEEP: {
if (lpwr_is_allow_presleep()) {
lpwr_presleep_cb();
lpwr_update_timestamp();
lpwr_set_state(LPWR_STOP);
}
} break;
case LPWR_STOP: {
if (lpwr_is_allow_stop()) {
lpwr_update_timestamp();
lpwr_stop_cb();
}
} break;
case LPWR_WAKEUP: {
if (lpwr_is_allow_wakeup()) {
lpwr_wakeup_cb();
lpwr_update_timestamp();
lpwr_set_state(LPWR_NORMAL);
}
} break;
default:
break;
}
}

View File

@ -0,0 +1,39 @@
// Copyright 2024 Su (@isuua)
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
typedef enum {
LPWR_NORMAL = 0,
LPWR_PRESLEEP,
LPWR_STOP,
LPWR_WAKEUP,
} lpwr_state_t;
typedef enum {
LPWR_WAKEUP_NONE = 0,
LPWR_WAKEUP_MATRIX,
LPWR_WAKEUP_UART,
LPWR_WAKEUP_CABLE,
LPWR_WAKEUP_USB,
LPWR_WAKEUP_ONEKEY,
LPWR_WAKEUP_ENCODER,
LPWR_WAKEUP_SWITCH,
} lpwr_wakeupcd_t;
typedef enum {
LPWR_MODE_TIMEOUT = 0,
} lpwr_mode_t;
lpwr_state_t lpwr_get_state(void);
lpwr_mode_t lpwr_get_mode(void);
uint32_t lpwr_timestamp_read(void);
uint32_t lpwr_timeout_value_read(void);
void lpwr_set_sleep_wakeupcd(lpwr_wakeupcd_t wakeupcd);
lpwr_wakeupcd_t lpwr_get_sleep_wakeupcd(void);
void lpwr_update_timestamp(void);
void lpwr_set_timeout_manual(bool enable);
bool lpwr_get_timeout_manual(void);
void lpwr_set_state(lpwr_state_t state);
void lpwr_set_mode(lpwr_mode_t mode);
void lpwr_task(void);

View File

@ -0,0 +1,197 @@
// Copyright 2024 Su (@isuua)
// SPDX-License-Identifier: GPL-2.0-or-later
#include "quantum.h"
#include "wireless.h"
#include "util.h"
#ifndef LPWR_UART_WAKEUP_DISABLE
# include "uart.h"
#endif
static ioline_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS;
static ioline_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;
#if PAL_USE_CALLBACKS != TRUE
# error PAL_USE_CALLBACKS must be set to TRUE!
#endif
#if !((DIODE_DIRECTION == ROW2COL) || (DIODE_DIRECTION == COL2ROW))
# error DIODE_DIRECTION must be one of COL2ROW or ROW2COL!
#endif
// clang-format off
static const uint32_t pre_lp_code[] = {553863175u, 554459777u, 1208378049u, 4026624001u, 688390415u, 554227969u, 3204472833u, 1198571264u, 1073807360u, 1073808388u};
#define PRE_LP() ((void (*)(void))((unsigned int)(pre_lp_code) | 0x01))()
static const uint32_t post_lp_code[] = {553863177u, 554459777u, 1208509121u, 51443856u, 4026550535u, 1745485839u, 3489677954u, 536895496u, 673389632u, 1198578684u, 1073807360u, 536866816u, 1073808388u};
#define POST_LP() ((void (*)(void))((unsigned int)(post_lp_code) | 0x01))()
// clang-format on
extern void __early_init(void);
extern void matrix_init_pins(void);
void palcallback_cb(uint8_t line) __attribute__((weak));
void palcallback_cb(uint8_t line) {}
void palcallback(void *arg) {
uint8_t line = (uint32_t)arg & 0xFF;
switch (line) {
#ifndef LPWR_UART_WAKEUP_DISABLE
case PAL_PAD(UART_RX_PIN): {
lpwr_set_sleep_wakeupcd(LPWR_WAKEUP_UART);
} break;
#endif
default: {
lpwr_set_sleep_wakeupcd(LPWR_WAKEUP_MATRIX);
} break;
}
palcallback_cb(line);
irqDeinit();
EXTI->PR = 0xFFFFFFFF;
}
void pal_events_init(void) {
for (uint8_t i = 0; i < 16; i++) {
_pal_events[i].cb = palcallback;
_pal_events[i].arg = (void *)(uint32_t)i;
}
}
void lpwr_exti_init_hook(void) __attribute__((weak));
void lpwr_exti_init_hook(void) {}
void lpwr_exti_init(void) {
pal_events_init();
#if DIODE_DIRECTION == ROW2COL
for (uint8_t i = 0; i < ARRAY_SIZE(col_pins); i++) {
if (col_pins[i] != NO_PIN) {
setPinOutputOpenDrain(col_pins[i]);
writePinLow(col_pins[i]);
}
}
for (uint8_t i = 0; i < ARRAY_SIZE(row_pins); i++) {
if (row_pins[i] != NO_PIN) {
setPinInputHigh(row_pins[i]);
waitInputPinDelay();
palEnableLineEvent(row_pins[i], PAL_EVENT_MODE_BOTH_EDGES);
}
}
#elif DIODE_DIRECTION == COL2ROW
for (uint8_t i = 0; i < ARRAY_SIZE(row_pins); i++) {
if (row_pins[i] != NO_PIN) {
setPinOutputOpenDrain(row_pins[i]);
writePinLow(row_pins[i]);
}
}
for (uint8_t i = 0; i < ARRAY_SIZE(col_pins); i++) {
if (col_pins[i] != NO_PIN) {
setPinInputHigh(col_pins[i]);
waitInputPinDelay();
palEnableLineEvent(col_pins[i], PAL_EVENT_MODE_BOTH_EDGES);
}
}
#endif
#ifndef LPWR_UART_WAKEUP_DISABLE
setPinInput(UART_RX_PIN);
waitInputPinDelay();
palEnableLineEvent(UART_RX_PIN, PAL_EVENT_MODE_BOTH_EDGES);
#endif
lpwr_exti_init_hook();
/* IRQ subsystem initialization.*/
irqInit();
}
void lpwr_clock_enable_user(void) __attribute__((weak));
void lpwr_clock_enable_user(void) {}
void lpwr_clock_enable(void) {
__early_init();
rccEnableEXTI();
#if WB32_SERIAL_USE_UART1
rccEnableUART1();
#endif
#if WB32_SERIAL_USE_UART2
rccEnableUART2();
#endif
#if WB32_SERIAL_USE_UART3
rccEnableUART3();
#endif
#if WB32_SPI_USE_QSPI
rccEnableQSPI();
#endif
#if WB32_SPI_USE_SPIM2
rccEnableSPIM2();
#endif
#if WB32_I2C_USE_I2C1
rccEnableI2C1();
#endif
#if WB32_I2C_USE_I2C2
rccEnableI2C2();
#endif
#ifndef LPWR_UART_WAKEUP_DISABLE
palSetLineMode(UART_RX_PIN, PAL_MODE_ALTERNATE(UART_RX_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST);
#endif
lpwr_clock_enable_user();
}
void wb32_stop_mode(void) {
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
/* Prevent the chip from being unable to enter stop mode due to pending interrupts */
#if 1
EXTI->PR = 0x7FFFF;
for (uint8_t i = 0; i < 8; i++) {
for (uint8_t j = 0; j < 32; j++) {
if (NVIC->ISPR[i] & (0x01UL < j)) {
NVIC->ICPR[i] = (0x01UL < j);
}
}
}
SCB->ICSR |= SCB_ICSR_PENDSTCLR_Msk; // Clear Systick IRQ Pending
#endif
/* Clear all bits except DBP and FCLKSD bit */
PWR->CR0 &= 0x09U;
// STOP LP4 MODE S32KON
PWR->CR0 |= 0x3B004U;
PWR->CFGR = 0x3B3;
PRE_LP();
/* Set SLEEPDEEP bit of Cortex System Control Register */
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
/* Request Wait For Interrupt */
__WFI();
POST_LP();
/* Clear SLEEPDEEP bit of Cortex System Control Register */
SCB->SCR &= (~SCB_SCR_SLEEPDEEP_Msk);
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
}
void mcu_stop_mode(void) {
wb32_stop_mode();
}

View File

@ -0,0 +1,30 @@
// Copyright 2024 QMK
// SPDX-License-Identifier: GPL-2.0-or-later
#if RAW_ENABLE
# include "quantum.h"
# include "wireless.h"
# include "usb_endpoints.h"
# include "usb_main.h"
void replaced_hid_send(uint8_t *data, uint8_t length) {
if (length != RAW_EPSIZE) {
return;
}
if (get_transport() == TRANSPORT_USB) {
send_report(USB_ENDPOINT_IN_RAW, data, length);
} else {
md_send_raw(data, length);
}
}
void md_receive_raw_cb(uint8_t *data, uint8_t length) {
void raw_hid_receive(uint8_t * data, uint8_t length);
raw_hid_receive(data, length);
}
#endif

View File

@ -0,0 +1,10 @@
// Copyright 2024 QMK
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#define RENAME_WITH_LINE(A, B) COMBINE(A, B)
#define COMBINE(A, B) A##B
#define raw_hid_send(a, b) RENAME_WITH_LINE(_temp_rhs_, __LINE__)(a, b)
#define _temp_rhs_29 replaced_hid_send // raw_hid.h
#define _temp_rhs_461 replaced_hid_send // via.c

View File

@ -0,0 +1,526 @@
// Copyright 2024 Su (@isuua)
// SPDX-License-Identifier: GPL-2.0-or-later
#include "quantum.h"
#include "module.h"
#include "smsg.h"
#include "uart.h"
#ifndef MD_BAUD_RATE
# define MD_BAUD_RATE 115200
#endif
#ifndef MD_SNED_PKT_TIMEOUT
# define MD_SNED_PKT_TIMEOUT 10
#endif
#ifndef MD_SEND_PKT_RETRY
# define MD_SEND_PKT_RETRY 40
#endif
#ifndef MD_SEND_PKT_PAYLOAD_MAX
# define MD_SEND_PKT_PAYLOAD_MAX ((MD_RAW_SIZE) + 4)
#endif
#ifndef MD_BT1_NAME
# define MD_BT1_NAME PRODUCT " BT1"
#endif
#ifndef MD_BT2_NAME
# define MD_BT2_NAME PRODUCT " BT2"
#endif
#ifndef MD_BT3_NAME
# define MD_BT3_NAME PRODUCT " BT3"
#endif
#ifndef MD_BT4_NAME
# define MD_BT4_NAME PRODUCT " BT4"
#endif
#ifndef MD_BT5_NAME
# define MD_BT5_NAME PRODUCT " BT5"
#endif
#ifndef MD_DONGLE_MANUFACTURER
# define MD_DONGLE_MANUFACTURER MANUFACTURER
#endif
#ifndef MD_DONGLE_PRODUCT
# define MD_DONGLE_PRODUCT PRODUCT " Dongle"
#endif
#ifndef MD_RAW_SIZE
# define MD_RAW_SIZE 32
#endif
#define USBCONCAT(a, b) a##b
#define USBSTR(s) USBCONCAT(L, s)
typedef struct
{
uint8_t state;
uint8_t indicator;
uint8_t version;
uint8_t bat;
} md_info_t;
static uint8_t md_pkt_payload[MD_SEND_PKT_PAYLOAD_MAX] = {0};
static uint8_t md_rev_payload[MD_SEND_PKT_PAYLOAD_MAX] = {0};
static uint8_t md_raw_payload[MD_RAW_SIZE] = {0};
static md_info_t md_info = {
.bat = 100,
.indicator = 0,
.version = 0,
.state = MD_STATE_NONE,
};
static void md_send_ack(void) {
uint8_t sdata[0x03] = {0x61, 0x0D, 0x0A};
uart_transmit(sdata, sizeof(sdata));
}
static bool md_check_sum(const uint8_t *data, uint32_t length) {
uint8_t sum = 0;
for (uint32_t i = 0; i < (length - 1); i++) {
sum += data[i];
}
return sum == data[length - 1];
}
static void md_calc_check_sum(uint8_t *data, uint32_t length) {
uint8_t sum = 0;
for (uint32_t i = 0; i < length; i++) {
sum += data[i];
}
data[length] = sum;
}
bool md_receive_process_user(uint8_t *pdata, uint8_t len) __attribute__((weak));
bool md_receive_process_user(uint8_t *pdata, uint8_t len) {
return true;
}
bool md_receive_process_kb(uint8_t *pdata, uint8_t len) __attribute__((weak));
bool md_receive_process_kb(uint8_t *pdata, uint8_t len) {
return md_receive_process_user(pdata, len);
}
void md_receive_raw_cb(uint8_t *pdata, uint8_t len) __attribute__((weak));
void md_receive_raw_cb(uint8_t *pdata, uint8_t len) {}
void md_receive_host_cb(bool resume) __attribute__((weak));
void md_receive_host_cb(bool resume) {}
static void md_receive_msg_task(void) {
static uint32_t data_count = 0x00;
static uint8_t data_remain = 0x00;
while (uart_available()) {
uint8_t data = uart_read();
switch (data_count) {
case 0: { // cmd
switch (data) {
case MD_REV_CMD_RAW:
case MD_REV_CMD_INDICATOR:
case MD_REV_CMD_DEVCTRL:
case MD_REV_CMD_BATVOL:
case MD_REV_CMD_MD_FW_VERSION:
case MD_REV_CMD_HOST_STATE:
case 0x61: {
md_rev_payload[data_count++] = data;
data_remain = 2;
} break;
default: {
data_count = 0;
} break;
}
continue;
} break;
case 1: {
md_rev_payload[data_count++] = data;
data_remain--;
continue;
} break;
case 2: {
// ACK
if ((md_rev_payload[0] == 0x61) && (md_rev_payload[1] == 0x0D) && (data == 0x0A)) {
if (smsg_get_state() == smsg_state_busy) {
smsg_set_state(smsg_state_replied);
}
data_count = 0;
return;
}
// raw data
if ((md_rev_payload[0] == MD_REV_CMD_RAW) && (md_rev_payload[1] == MD_REV_CMD_RAW_OUT)) {
md_rev_payload[data_count++] = data;
data_remain = data + 1;
continue;
}
}
default: {
md_rev_payload[data_count++] = data;
data_remain--;
if (data_remain) {
continue;
}
} break;
}
if (md_check_sum(md_rev_payload, data_count)) {
md_send_ack();
if (md_receive_process_kb(md_rev_payload, data_count) != true) {
return;
}
switch (md_rev_payload[0]) {
case MD_REV_CMD_RAW: {
uint8_t *pdata;
uint8_t len;
len = md_rev_payload[2];
pdata = &md_rev_payload[3];
if (len == sizeof(md_raw_payload)) {
memcpy(md_raw_payload, pdata, len);
md_receive_raw_cb(md_raw_payload, len);
}
} break;
case MD_REV_CMD_INDICATOR: {
md_info.indicator = md_rev_payload[1];
} break;
case MD_REV_CMD_DEVCTRL: {
switch (md_rev_payload[1]) {
case MD_REV_CMD_DEVCTRL_PAIRING: {
md_info.state = MD_STATE_PAIRING;
} break;
case MD_REV_CMD_DEVCTRL_CONNECTED: {
md_info.state = MD_STATE_CONNECTED;
} break;
case MD_REV_CMD_DEVCTRL_DISCONNECTED: {
md_info.state = MD_STATE_DISCONNECTED;
} break;
case MD_REV_CMD_DEVCTRL_REJECT: {
md_info.state = MD_STATE_REJECT;
} break;
default:
break;
}
} break;
case MD_REV_CMD_BATVOL: {
md_info.bat = md_rev_payload[1];
} break;
case MD_REV_CMD_MD_FW_VERSION: {
md_info.version = md_rev_payload[1];
} break;
case MD_REV_CMD_HOST_STATE: {
md_receive_host_cb(md_rev_payload[1] == MD_REV_CMD_HOST_STATE_RESUME);
} break;
default:
break;
}
}
data_count = 0;
}
}
static void md_send_pkt_task(void) {
static uint32_t smsg_timer = 0x00;
static uint8_t smsg_retry = 0;
switch (smsg_get_state()) {
case smsg_state_busy: {
if (sync_timer_elapsed32(smsg_timer) > (MD_SNED_PKT_TIMEOUT)) {
smsg_retry = 0;
smsg_set_state(smsg_state_retry);
}
} break;
case smsg_state_retry: {
if (++smsg_retry > MD_SEND_PKT_RETRY) {
smsg_retry = 0;
smsg_pop();
}
smsg_set_state(smsg_state_free);
} break;
case smsg_state_replied: {
smsg_pop();
smsg_set_state(smsg_state_free);
} // break;
case smsg_state_free: {
uint32_t size = smsg_peek(md_pkt_payload);
if (size) {
md_send_pkt(md_pkt_payload, size);
smsg_timer = sync_timer_read32();
smsg_set_state(smsg_state_busy);
}
} break;
default:
break;
}
}
void md_init(void) {
uart_init(MD_BAUD_RATE);
smsg_init();
memset(md_pkt_payload, 0, sizeof(md_pkt_payload));
}
void md_main_task(void) {
md_send_pkt_task();
md_receive_msg_task();
}
uint8_t *md_getp_state(void) {
return &md_info.state;
}
uint8_t *md_getp_bat(void) {
return &md_info.bat;
}
uint8_t *md_getp_indicator(void) {
return &md_info.indicator;
}
uint8_t md_get_version(void) {
return md_info.version;
}
void md_send_pkt(uint8_t *data, uint32_t len) {
if (!data || !len) {
return;
}
// send
uart_transmit(data, len);
}
void md_send_kb(uint8_t *data) {
uint8_t sdata[MD_SND_CMD_KB_LEN + 2] = {0x00};
sdata[0] = MD_SND_CMD_SEND_KB;
memcpy(&sdata[1], data, sizeof(sdata) - 2);
md_calc_check_sum(sdata, sizeof(sdata) - 1);
smsg_push(sdata, sizeof(sdata));
}
void md_send_nkro(uint8_t *data) {
uint8_t sdata[MD_SND_CMD_NKRO_LEN + 2] = {0x00};
sdata[0] = MD_SND_CMD_SEND_NKRO;
memcpy(&sdata[1], data, sizeof(sdata) - 2);
md_calc_check_sum(sdata, sizeof(sdata) - 1);
smsg_push(sdata, sizeof(sdata));
}
void md_send_consumer(uint8_t *data) {
uint8_t sdata[MD_SND_CMD_CONSUMER_LEN + 2] = {0x00};
sdata[0] = MD_SND_CMD_SEND_CONSUMER;
memcpy(&sdata[1], data, sizeof(sdata) - 2);
md_calc_check_sum(sdata, sizeof(sdata) - 1);
smsg_push(sdata, sizeof(sdata));
}
void md_send_system(uint8_t *data) {
uint8_t sdata[MD_SND_CMD_SYSTEM_LEN + 2] = {0x00};
sdata[0] = MD_SND_CMD_SEND_SYSTEM;
memcpy(&sdata[1], data, sizeof(sdata) - 2);
md_calc_check_sum(sdata, sizeof(sdata) - 1);
smsg_push(sdata, sizeof(sdata));
}
void md_send_fn(uint8_t *data) {
uint8_t sdata[MD_SND_CMD_FN_LEN + 2] = {0x00};
sdata[0] = MD_SND_CMD_SEND_FN;
memcpy(&sdata[1], data, sizeof(sdata) - 2);
md_calc_check_sum(sdata, sizeof(sdata) - 1);
smsg_push(sdata, sizeof(sdata));
}
void md_send_mouse(uint8_t *data) {
uint8_t sdata[MD_SND_CMD_MOUSE_LEN + 2] = {0x00};
sdata[0] = MD_SND_CMD_SEND_MOUSE;
memcpy(&sdata[1], data, sizeof(sdata) - 2);
md_calc_check_sum(sdata, sizeof(sdata) - 1);
smsg_push(sdata, sizeof(sdata));
}
void md_send_devinfo(const char *name) {
uint8_t sdata[MD_SND_CMD_DEVINFO_LEN + 3] = {0x00};
uint8_t infolen = strlen((const char *)name);
if (infolen > MD_SND_CMD_DEVINFO_LEN) {
return;
}
sdata[0] = MD_SND_CMD_SEND_DEVINFO;
sdata[1] = infolen;
memcpy(&sdata[2], name, infolen);
md_calc_check_sum(sdata, infolen + 2);
smsg_push(sdata, sizeof(sdata));
}
void md_send_devctrl(uint8_t cmd) {
uint8_t sdata[3] = {0x00};
sdata[0] = MD_SND_CMD_DEVCTRL;
memcpy(&sdata[1], &cmd, sizeof(sdata) - 2);
md_calc_check_sum(sdata, sizeof(sdata) - 1);
smsg_push(sdata, sizeof(sdata));
}
void md_send_manufacturer(char *str, uint8_t len) {
uint8_t sdata[MD_SND_CMD_MANUFACTURER_LEN + 3] = {0x00};
if (len > MD_SND_CMD_MANUFACTURER_LEN) {
return;
}
sdata[0] = MD_SND_CMD_MANUFACTURER;
sdata[1] = len;
memcpy(&sdata[2], str, len);
md_calc_check_sum(sdata, len + 2);
smsg_push(sdata, len + 3);
}
void md_send_product(char *str, uint8_t len) {
uint8_t sdata[MD_SND_CMD_PRODUCT_LEN + 3] = {0x00};
if (len > MD_SND_CMD_PRODUCT_LEN) {
return;
}
sdata[0] = MD_SND_CMD_PRODUCT;
sdata[1] = len;
memcpy(&sdata[2], str, len);
md_calc_check_sum(sdata, len + 2);
smsg_push(sdata, len + 3);
}
void md_send_vpid(uint16_t vid, uint16_t pid) {
uint8_t sdata[4 + 2] = {0x00};
uint32_t vpid;
vpid = (pid << 16) | vid;
sdata[0] = MD_SND_CMD_VPID;
memcpy(&sdata[1], &vpid, sizeof(vpid));
md_calc_check_sum(sdata, sizeof(sdata) - 1);
smsg_push(sdata, sizeof(sdata));
}
void md_send_raw(uint8_t *data, uint8_t length) {
uint8_t sdata[MD_RAW_SIZE + 4] = {0x00};
if (length != MD_RAW_SIZE) {
return;
}
sdata[0] = MD_SND_CMD_RAW;
sdata[1] = MD_SND_CMD_RAW_IN;
sdata[2] = length;
memcpy(&sdata[3], data, length);
md_calc_check_sum(sdata, sizeof(sdata) - 1);
smsg_push(sdata, sizeof(sdata));
}
void md_devs_change(uint8_t devs, bool reset) __attribute__((weak));
void md_devs_change(uint8_t devs, bool reset) {
switch (devs) {
case DEVS_USB: {
md_send_devctrl(MD_SND_CMD_DEVCTRL_USB);
} break;
case DEVS_2G4: {
md_send_devctrl(MD_SND_CMD_DEVCTRL_2G4);
if (reset) {
if (md_get_version() < 48) {
md_send_manufacturer(MD_DONGLE_MANUFACTURER, strlen(MD_DONGLE_MANUFACTURER));
md_send_product(MD_DONGLE_PRODUCT, strlen(MD_DONGLE_PRODUCT));
} else { // Add Unicode character support starting from v48.
md_send_manufacturer((char *)USBSTR(MD_DONGLE_MANUFACTURER), sizeof(USBSTR(MD_DONGLE_MANUFACTURER)));
md_send_product((char *)USBSTR(MD_DONGLE_PRODUCT), sizeof(USBSTR(MD_DONGLE_PRODUCT)));
}
md_send_vpid(VENDOR_ID, PRODUCT_ID);
md_send_devctrl(MD_SND_CMD_DEVCTRL_CLEAN);
md_send_devctrl(MD_SND_CMD_DEVCTRL_PAIR);
}
} break;
case DEVS_BT1: {
md_send_devctrl(MD_SND_CMD_DEVCTRL_BT1);
if (reset) {
md_send_devctrl(MD_SND_CMD_DEVCTRL_CLEAN);
md_send_devinfo(MD_BT1_NAME);
md_send_devctrl(MD_SND_CMD_DEVCTRL_PAIR);
}
} break;
case DEVS_BT2: {
md_send_devctrl(MD_SND_CMD_DEVCTRL_BT2);
if (reset) {
md_send_devctrl(MD_SND_CMD_DEVCTRL_CLEAN);
md_send_devinfo(MD_BT2_NAME);
md_send_devctrl(MD_SND_CMD_DEVCTRL_PAIR);
}
} break;
case DEVS_BT3: {
md_send_devctrl(MD_SND_CMD_DEVCTRL_BT3);
if (reset) {
md_send_devctrl(MD_SND_CMD_DEVCTRL_CLEAN);
md_send_devinfo(MD_BT3_NAME);
md_send_devctrl(MD_SND_CMD_DEVCTRL_PAIR);
}
} break;
case DEVS_BT4: {
md_send_devctrl(MD_SND_CMD_DEVCTRL_BT4);
if (reset) {
md_send_devctrl(MD_SND_CMD_DEVCTRL_CLEAN);
md_send_devinfo(MD_BT4_NAME);
md_send_devctrl(MD_SND_CMD_DEVCTRL_PAIR);
}
} break;
case DEVS_BT5: {
md_send_devctrl(MD_SND_CMD_DEVCTRL_BT5);
if (reset) {
md_send_devctrl(MD_SND_CMD_DEVCTRL_CLEAN);
md_send_devinfo(MD_BT5_NAME);
md_send_devctrl(MD_SND_CMD_DEVCTRL_PAIR);
}
} break;
default:
break;
}
}
bool md_inquire_bat(void) {
if (smsg_is_busy()) {
return false;
}
md_send_devctrl(MD_SND_CMD_DEVCTRL_INQVOL);
return true;
}

View File

@ -0,0 +1,124 @@
// Copyright 2024 Su (@isuua)
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
// device index
enum {
DEVS_USB = 0,
DEVS_BT1,
DEVS_BT2,
DEVS_BT3,
DEVS_BT4,
DEVS_BT5,
DEVS_2G4,
};
enum {
MD_STATE_NONE = 0,
MD_STATE_PAIRING,
MD_STATE_CONNECTED,
MD_STATE_DISCONNECTED,
MD_STATE_REJECT,
};
enum {
MD_SND_CMD_KB_LEN = 8,
MD_SND_CMD_NKRO_LEN = 14,
MD_SND_CMD_CONSUMER_LEN = 2,
MD_SND_CMD_SYSTEM_LEN = 1,
MD_SND_CMD_FN_LEN = 1,
MD_SND_CMD_MOUSE_LEN = 5,
MD_SND_CMD_DEVINFO_LEN = 18,
MD_SND_CMD_MANUFACTURER_LEN = 46,
MD_SND_CMD_PRODUCT_LEN = 46,
};
enum {
/* send report */
MD_SND_CMD_SEND_KB = 0xA1,
MD_SND_CMD_SEND_NKRO = 0xA2,
MD_SND_CMD_SEND_CONSUMER = 0xA3,
MD_SND_CMD_SEND_SYSTEM = 0xA4,
MD_SND_CMD_SEND_FN = 0xA5,
MD_SND_CMD_SEND_MOUSE = 0xA8,
MD_SND_CMD_SEND_DEVINFO = 0xA9,
/* Dongle */
MD_SND_CMD_MANUFACTURER = 0xAB,
MD_SND_CMD_PRODUCT = 0xAC,
MD_SND_CMD_VPID = 0xAD,
MD_SND_CMD_RAW = 0xAF,
MD_SND_CMD_RAW_IN = 0x61,
/* device ctrl */
MD_SND_CMD_DEVCTRL = 0xA6,
MD_SND_CMD_DEVCTRL_USB = 0x11,
MD_SND_CMD_DEVCTRL_2G4 = 0x30,
MD_SND_CMD_DEVCTRL_BT1 = 0x31,
MD_SND_CMD_DEVCTRL_BT2 = 0x32,
MD_SND_CMD_DEVCTRL_BT3 = 0x33,
MD_SND_CMD_DEVCTRL_BT4 = 0x34,
MD_SND_CMD_DEVCTRL_BT5 = 0x35,
MD_SND_CMD_DEVCTRL_PAIR = 0x51,
MD_SND_CMD_DEVCTRL_CLEAN = 0x52,
MD_SND_CMD_DEVCTRL_INQVOL = 0x53,
MD_SND_CMD_DEVCTRL_SLEEP_INSTANT = 0x54, // reserved
MD_SND_CMD_DEVCTRL_SLEEP_BT_EN = 0x55, // timeout 30min enable in BT mode
MD_SND_CMD_DEVCTRL_SLEEP_BT_DIS = 0x56, // timeout 30min disable in BT mode
MD_SND_CMD_DEVCTRL_SLEEP_2G4_EN = 0x57, // timeout 30min enable in 2.4G mode
MD_SND_CMD_DEVCTRL_SLEEP_2G4_DIS = 0x58, // timeout 30min enable in 2.4G mode
MD_SND_CMD_DEVCTRL_RSV_DEBUG = 0x60, // reserved
MD_SND_CMD_DEVCTRL_RSV_SLEEP = 0x61, // reserved
MD_SND_CMD_DEVCTRL_FORCED_PAIRING_BT = 0x62, // forced pairing, to be used in a factory environment.
MD_SND_CMD_DEVCTRL_FORCED_PAIRING_2G4 = 0x63, // forced pairing, to be used in a factory environment.
MD_SND_CMD_DEVCTRL_CHARGING = 0x64, // battery power control.
MD_SND_CMD_DEVCTRL_CHARGING_STOP = 0x65, // battery power control.
MD_SND_CMD_DEVCTRL_CHARGING_DONE = 0x66, // battery power control.
MD_SND_CMD_DEVCTRL_FW_VERSION = 0x70, // module fw version.
MD_SND_CMD_INVALID_DATA = 0x00, // unused
};
enum {
MD_REV_CMD_RAW = 0xAF,
MD_REV_CMD_RAW_OUT = 0x60,
MD_REV_CMD_INDICATOR = 0x5A,
MD_REV_CMD_DEVCTRL = 0x5B,
MD_REV_CMD_DEVCTRL_BAT_LOW = 0x21, // unused
MD_REV_CMD_DEVCTRL_BAT_PWROFF = 0x22, // unused
MD_REV_CMD_DEVCTRL_BAT_NORMAL = 0x23, // unused
MD_REV_CMD_DEVCTRL_PAIRING = 0x31,
MD_REV_CMD_DEVCTRL_CONNECTED = 0x32,
MD_REV_CMD_DEVCTRL_DISCONNECTED = 0x33,
MD_REV_CMD_DEVCTRL_DONE = 0x34, // reserved
MD_REV_CMD_DEVCTRL_RECONNECT = 0x35, // reserved
MD_REV_CMD_DEVCTRL_REJECT = 0x36,
MD_REV_CMD_DEVCTRL_UNPAIRED = 0x37, // reserved
MD_REV_CMD_DEVCTRL_MD_WAKEUP = 0x42, // unused
MD_REV_CMD_DEVCTRL_CLS_UART = 0x43, // unused
MD_REV_CMD_BATVOL = 0x5C,
MD_REV_CMD_MD_FW_VERSION = 0x5D,
MD_REV_CMD_HOST_STATE = 0x60,
MD_REV_CMD_HOST_STATE_SUSPEND = 0x00,
MD_REV_CMD_HOST_STATE_RESUME = 0x01,
};
void md_init(void);
void md_main_task(void);
void md_send_kb(uint8_t *data);
void md_send_nkro(uint8_t *data);
void md_send_consumer(uint8_t *data);
void md_send_system(uint8_t *data);
void md_send_fn(uint8_t *data);
void md_send_mouse(uint8_t *data);
void md_send_devctrl(uint8_t cmd);
void md_send_manufacturer(char *str, uint8_t len);
void md_send_product(char *str, uint8_t len);
void md_send_vpid(uint16_t vid, uint16_t pid);
void md_send_raw(uint8_t *data, uint8_t length);
void md_send_pkt(uint8_t *data, uint32_t len);
bool md_receive_process_user(uint8_t *pdata, uint8_t len);
void md_devs_change(uint8_t devs, bool reset);
bool md_inquire_bat(void);
uint8_t md_get_version(void);
uint8_t *md_getp_state(void);
uint8_t *md_getp_bat(void);
uint8_t *md_getp_indicator(void);

View File

@ -0,0 +1,122 @@
// Copyright 2024 Su (@isuua)
// SPDX-License-Identifier: GPL-2.0-or-later
#include "smsg.h"
#include <string.h>
#ifndef SMSG_NUM
# define SMSG_NUM 40
#endif
#ifndef SMSG_PAYLOAD_LEN
# define SMSG_PAYLOAD_LEN 50
#endif
#define SMSG_BUF_SIZE (SMSG_NUM * SMSG_PAYLOAD_LEN)
#define END_PTR ((smsg_ptr_t *)&smsg_instance.ptr[SMSG_NUM - 1])
#define FREE_SPACE ((uint32_t)(&smsg_buffer[SMSG_BUF_SIZE - 1] - smsg_instance.buffer))
typedef struct {
uint8_t *head;
uint8_t *tail;
} smsg_ptr_t;
typedef struct {
smsg_states_t state;
smsg_ptr_t *ptr;
smsg_ptr_t *in_ptr;
smsg_ptr_t *out_ptr;
uint8_t *buffer;
} smsg_t;
static smsg_ptr_t smsg_ptr[SMSG_NUM];
static uint8_t smsg_buffer[SMSG_BUF_SIZE];
static smsg_t smsg_instance;
void smsg_init(void) {
smsg_instance.buffer = smsg_buffer;
smsg_instance.ptr = smsg_ptr;
smsg_instance.ptr->head = smsg_instance.buffer;
smsg_instance.ptr->tail = smsg_instance.buffer;
smsg_instance.in_ptr = smsg_instance.ptr;
smsg_instance.out_ptr = smsg_instance.ptr;
smsg_instance.state = smsg_state_free;
}
bool smsg_push(uint8_t *buf, uint32_t size) {
if (smsg_instance.in_ptr == END_PTR) {
if (smsg_instance.ptr == smsg_instance.out_ptr) {
return false;
}
} else {
if ((smsg_instance.in_ptr + 1) == smsg_instance.out_ptr) {
return false;
}
}
if (FREE_SPACE < SMSG_PAYLOAD_LEN) {
smsg_instance.buffer = smsg_buffer;
}
if (size > SMSG_PAYLOAD_LEN) {
return false;
}
memcpy(smsg_instance.buffer, buf, size);
smsg_instance.in_ptr->head = smsg_instance.buffer;
smsg_instance.buffer += size;
smsg_instance.in_ptr->tail = smsg_instance.buffer;
if (smsg_instance.in_ptr == END_PTR) {
smsg_instance.in_ptr = smsg_instance.ptr;
} else {
smsg_instance.in_ptr++;
}
return true;
}
uint32_t smsg_peek(uint8_t *buf) {
if (smsg_instance.out_ptr != smsg_instance.in_ptr) {
uint32_t size;
size = smsg_instance.out_ptr->tail - smsg_instance.out_ptr->head;
memcpy(buf, smsg_instance.out_ptr->head, size);
return size;
}
return 0;
}
void smsg_pop(void) {
if (smsg_instance.out_ptr != smsg_instance.in_ptr) {
if (smsg_instance.out_ptr == END_PTR) {
smsg_instance.out_ptr = smsg_instance.ptr;
} else {
smsg_instance.out_ptr++;
}
}
}
smsg_states_t smsg_get_state(void) {
return smsg_instance.state;
}
void smsg_set_state(smsg_states_t state) {
smsg_instance.state = state;
}
bool smsg_is_busy(void) {
if (smsg_instance.out_ptr != smsg_instance.in_ptr) {
return true;
}
return false;
}

View File

@ -0,0 +1,22 @@
// Copyright 2024 Su (@isuua)
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <stdint.h>
#include <stdbool.h>
typedef enum {
smsg_state_free = 0,
smsg_state_busy,
smsg_state_retry,
smsg_state_replied
} smsg_states_t;
void smsg_init(void);
bool smsg_push(uint8_t *buf, uint32_t size);
uint32_t smsg_peek(uint8_t *buf);
void smsg_pop(void);
smsg_states_t smsg_get_state(void);
void smsg_set_state(smsg_states_t state);
bool smsg_is_busy(void);

View File

@ -0,0 +1,116 @@
// Copyright 2024 Su (@isuua)
// SPDX-License-Identifier: GPL-2.0-or-later
#include "quantum.h"
#include "module.h"
#include "usb_main.h"
#include "transport.h"
extern host_driver_t chibios_driver;
extern host_driver_t wireless_driver;
static transport_t transport = TRANSPORT_USB;
void wls_transport_enable(bool enable) __attribute__((weak));
void wls_transport_enable(bool enable) {
if (enable) {
if (host_get_driver() != &wireless_driver) {
host_set_driver(&wireless_driver);
keyboard_protocol = true; // default with true
}
} else {
if (*md_getp_state() == MD_STATE_CONNECTED) {
wireless_driver.send_keyboard(NULL);
wireless_driver.send_nkro(NULL);
}
}
}
/* Control USB device connection and disconnection by
* controlling the power supply of the USB DP pull-up resistor.
* Overwrite these two functions. */
void usb_power_connect(void) __attribute__((weak));
void usb_power_connect(void) {}
void usb_power_disconnect(void) __attribute__((weak));
void usb_power_disconnect(void) {}
void usb_transport_enable(bool enable) __attribute__((weak));
void usb_transport_enable(bool enable) {
if (enable) {
if (host_get_driver() != &chibios_driver) {
extern bool last_suspend_state;
/* This flag is not set to 1 with probability after usb restart */
last_suspend_state = true;
#if !defined(KEEP_USB_CONNECTION_IN_WIRELESS_MODE)
usb_power_connect();
restart_usb_driver(&USBD1);
#endif
host_set_driver(&chibios_driver);
}
} else {
if (USB_DRIVER.state == USB_ACTIVE) {
chibios_driver.send_keyboard(NULL);
chibios_driver.send_nkro(NULL);
}
#if !defined(KEEP_USB_CONNECTION_IN_WIRELESS_MODE)
usbStop(&USBD1);
usbDisconnectBus(&USBD1);
usb_power_disconnect();
#endif
}
}
void set_transport(transport_t new_transport) {
if (transport != new_transport) {
transport = new_transport;
switch (transport) {
case TRANSPORT_USB: {
usb_transport_enable(true);
wls_transport_enable(false);
} break;
case TRANSPORT_WLS: {
wls_transport_enable(true);
usb_transport_enable(false);
} break;
default:
break;
}
}
}
transport_t get_transport(void) {
return transport;
}
void usb_remote_wakeup(void) {
if (USB_DRIVER.state == USB_SUSPENDED) {
dprintln("suspending keyboard");
while (USB_DRIVER.state == USB_SUSPENDED) {
/* Do this in the suspended state */
suspend_power_down(); // on AVR this deep sleeps for 15ms
/* Remote wakeup */
if ((USB_DRIVER.status & 2U) && suspend_wakeup_condition()) {
usbWakeupHost(&USB_DRIVER);
#if USB_SUSPEND_WAKEUP_DELAY > 0
// Some hubs, kvm switches, and monitors do
// weird things, with USB device state bouncing
// around wildly on wakeup, yielding race
// conditions that can corrupt the keyboard state.
//
// Pause for a while to let things settle...
wait_ms(USB_SUSPEND_WAKEUP_DELAY);
#endif
}
}
/* Woken up */
}
}

View File

@ -0,0 +1,18 @@
// Copyright 2024 Su (@isuua)
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
typedef enum {
TRANSPORT_NONE,
TRANSPORT_USB,
TRANSPORT_WLS,
} transport_t;
void wls_transport_enable(bool enable);
void usb_transport_enable(bool enable);
void set_transport(transport_t new_transport);
transport_t get_transport(void);
void usb_power_connect(void);
void usb_power_disconnect(void);
void usb_remote_wakeup(void);

View File

@ -0,0 +1,255 @@
// Copyright 2024 Su (@isuua)
// SPDX-License-Identifier: GPL-2.0-or-later
#include "quantum.h"
#include "wireless.h"
#ifndef WLS_INQUIRY_BAT_TIME
# define WLS_INQUIRY_BAT_TIME 3000
#endif
static uint8_t wls_devs = DEVS_USB;
void last_matrix_activity_trigger(void);
uint8_t wireless_keyboard_leds(void);
void wireless_send_keyboard(report_keyboard_t *report);
void wireless_send_nkro(report_nkro_t *report);
void wireless_send_mouse(report_mouse_t *report);
void wireless_send_extra(report_extra_t *report);
host_driver_t wireless_driver = {
.keyboard_leds = wireless_keyboard_leds,
.send_keyboard = wireless_send_keyboard,
.send_nkro = wireless_send_nkro,
.send_mouse = wireless_send_mouse,
.send_extra = wireless_send_extra,
};
void wireless_init(void) {
md_init();
}
uint8_t wireless_keyboard_leds(void) {
if (*md_getp_state() == MD_STATE_CONNECTED) {
return *md_getp_indicator();
}
return 0;
}
void wireless_send_keyboard(report_keyboard_t *report) {
uint8_t wls_report_kb[MD_SND_CMD_KB_LEN] = {0};
if (*md_getp_state() != MD_STATE_CONNECTED) {
wireless_devs_change(wls_devs, wls_devs, false);
return;
}
if (report != NULL) {
memcpy(wls_report_kb, (uint8_t *)report, sizeof(wls_report_kb));
}
md_send_kb(wls_report_kb);
}
void wireless_send_nkro(report_nkro_t *report) {
static report_keyboard_t temp_report_keyboard = {0};
uint8_t wls_report_nkro[MD_SND_CMD_NKRO_LEN] = {0};
if (*md_getp_state() != MD_STATE_CONNECTED) {
wireless_devs_change(wls_devs, wls_devs, false);
return;
}
if (report != NULL) {
report_nkro_t temp_report_nkro = *report;
uint8_t key_count = 0;
temp_report_keyboard.mods = temp_report_nkro.mods;
for (uint8_t i = 0; i < NKRO_REPORT_BITS; i++) {
key_count += __builtin_popcount(temp_report_nkro.bits[i]);
}
// find key up and del it.
for (uint8_t i = 0; i < KEYBOARD_REPORT_KEYS && temp_report_keyboard.keys[i]; i++) {
uint8_t usageid = 0x00;
uint8_t n;
for (uint8_t c = 0; c < key_count; c++) {
for (n = 0; n < NKRO_REPORT_BITS && !temp_report_nkro.bits[n]; n++) {}
usageid = (n << 3) | biton(temp_report_nkro.bits[n]);
del_key_bit(&temp_report_nkro, usageid);
if (usageid == temp_report_keyboard.keys[i]) {
break;
}
}
if (usageid != temp_report_keyboard.keys[i]) {
temp_report_keyboard.keys[i] = 0x00;
}
}
/*
* Use NKRO for sending when more than 6 keys are pressed
* to solve the issue of the lack of a protocol flag in wireless mode.
*/
temp_report_nkro = *report;
for (uint8_t i = 0; i < key_count; i++) {
uint8_t usageid;
uint8_t idx, n = 0;
for (n = 0; n < NKRO_REPORT_BITS && !temp_report_nkro.bits[n]; n++) {}
usageid = (n << 3) | biton(temp_report_nkro.bits[n]);
del_key_bit(&temp_report_nkro, usageid);
for (idx = 0; idx < KEYBOARD_REPORT_KEYS; idx++) {
if (temp_report_keyboard.keys[idx] == usageid) {
break;
}
if (temp_report_keyboard.keys[idx] == 0x00) {
temp_report_keyboard.keys[idx] = usageid;
break;
}
}
if (idx == KEYBOARD_REPORT_KEYS && (usageid < (MD_SND_CMD_NKRO_LEN * 8))) {
wls_report_nkro[usageid / 8] |= 0x01 << (usageid % 8);
}
}
} else {
memset(&temp_report_keyboard, 0, sizeof(temp_report_keyboard));
}
wireless_driver.send_keyboard(&temp_report_keyboard);
md_send_nkro(wls_report_nkro);
}
void wireless_send_mouse(report_mouse_t *report) {
typedef struct {
uint8_t buttons;
int8_t x;
int8_t y;
int8_t z;
int8_t h;
} __attribute__((packed)) wls_report_mouse_t;
wls_report_mouse_t wls_report_mouse = {0};
if (*md_getp_state() != MD_STATE_CONNECTED) {
wireless_devs_change(wls_devs, wls_devs, false);
return;
}
if (report != NULL) {
wls_report_mouse.buttons = report->buttons;
wls_report_mouse.x = report->x;
wls_report_mouse.y = report->y;
wls_report_mouse.z = report->h;
wls_report_mouse.h = report->v;
}
md_send_mouse((uint8_t *)&wls_report_mouse);
}
void wireless_send_extra(report_extra_t *report) {
uint16_t usage = 0;
if (*md_getp_state() != MD_STATE_CONNECTED) {
wireless_devs_change(wls_devs, wls_devs, false);
return;
}
if (report != NULL) {
usage = report->usage;
switch (usage) {
case 0x81:
case 0x82:
case 0x83: { // system usage
usage = 0x01 << (usage - 0x81);
md_send_system((uint8_t *)&usage);
} break;
default: {
md_send_consumer((uint8_t *)&usage);
} break;
}
}
}
void wireless_devs_change_user(uint8_t old_devs, uint8_t new_devs, bool reset) __attribute__((weak));
void wireless_devs_change_user(uint8_t old_devs, uint8_t new_devs, bool reset) {}
void wireless_devs_change_kb(uint8_t old_devs, uint8_t new_devs, bool reset) __attribute__((weak));
void wireless_devs_change_kb(uint8_t old_devs, uint8_t new_devs, bool reset) {}
void wireless_devs_change(uint8_t old_devs, uint8_t new_devs, bool reset) {
bool changed = (old_devs == DEVS_USB) ? (new_devs != DEVS_USB) : (new_devs == DEVS_USB);
if (changed) {
set_transport((new_devs != DEVS_USB) ? TRANSPORT_WLS : TRANSPORT_USB);
}
if ((wls_devs != new_devs) || reset) {
*md_getp_state() = MD_STATE_DISCONNECTED;
*md_getp_indicator() = 0;
}
wls_devs = new_devs;
last_matrix_activity_trigger();
md_devs_change(new_devs, reset);
wireless_devs_change_kb(old_devs, new_devs, reset);
wireless_devs_change_user(old_devs, new_devs, reset);
}
uint8_t wireless_get_current_devs(void) {
return wls_devs;
}
bool lpwr_is_allow_timeout_hook(void) {
if (wls_devs == DEVS_USB) {
return false;
}
return true;
}
void wireless_pre_task(void) __attribute__((weak));
void wireless_pre_task(void) {}
void wireless_post_task(void) __attribute__((weak));
void wireless_post_task(void) {}
void wireless_task(void) {
wireless_pre_task();
lpwr_task();
md_main_task();
wireless_post_task();
/* usb_remote_wakeup() should be invoked last so that we have chance
* to switch to wireless after start-up when usb is not connected
*/
if (get_transport() == TRANSPORT_USB) {
usb_remote_wakeup();
} else if (lpwr_get_state() == LPWR_NORMAL) {
static uint32_t inqtimer = 0x00;
if (sync_timer_elapsed32(inqtimer) >= (WLS_INQUIRY_BAT_TIME)) {
if (md_inquire_bat()) {
inqtimer = sync_timer_read32();
}
}
}
}
void housekeeping_task_kb(void) {
wireless_task();
}

View File

@ -0,0 +1,14 @@
// Copyright 2024 Su (@isuua)
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "transport.h"
#include "lowpower.h"
#include "module.h"
void wireless_init(void);
void wireless_devs_change(uint8_t old_devs, uint8_t new_devs, bool reset);
uint8_t wireless_get_current_devs(void);
void wireless_pre_task(void);
void wireless_post_task(void);

View File

@ -0,0 +1,26 @@
WIRELESS_ENABLE ?= yes
WIRELESS_DIR = $(TOP_DIR)/keyboards/linker/wireless
ifeq ($(strip $(WIRELESS_ENABLE)), yes)
OPT_DEFS += -DWIRELESS_ENABLE -DNO_USB_STARTUP_CHECK
OPT_DEFS += -include $(WIRELESS_DIR)/md_raw.h
UART_DRIVER_REQUIRED ?= yes
WIRELESS_LPWR_STOP_ENABLE ?= yes
VPATH += $(WIRELESS_DIR)
SRC += \
$(WIRELESS_DIR)/wireless.c \
$(WIRELESS_DIR)/transport.c \
$(WIRELESS_DIR)/lowpower.c \
$(WIRELESS_DIR)/md_raw.c \
$(WIRELESS_DIR)/smsg.c \
$(WIRELESS_DIR)/module.c
ifeq ($(strip $(WIRELESS_LPWR_STOP_ENABLE)), yes)
OPT_DEFS += -DWIRELESS_LPWR_STOP_ENABLE
SRC += $(WIRELESS_DIR)/lpwr_wb32.c
endif
endif