drivers: input: Add CH9350L USB keyboard driver
Adds a driver that allows using CH9350 as a USB keyboard interface chip Signed-off-by: Camille BAUD <mail@massdriver.space>
This commit is contained in:
committed by
Anas Nashif
parent
0c97804b3d
commit
fd9b0b81b7
@@ -10,6 +10,7 @@ zephyr_library_sources_ifdef(CONFIG_INPUT_ANALOG_AXIS_SETTINGS input_analog_axis
|
||||
zephyr_library_sources_ifdef(CONFIG_INPUT_BFLB_IRX input_bflb_irx.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_INPUT_CAP12XX input_cap12xx.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_INPUT_CF1133 input_cf1133.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_INPUT_CH9350L input_ch9350l.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_INPUT_CHSC5X input_chsc5x.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_INPUT_CHSC6X input_chsc6x.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_INPUT_CST816S input_cst816s.c)
|
||||
|
||||
@@ -11,6 +11,7 @@ source "drivers/input/Kconfig.analog_axis"
|
||||
source "drivers/input/Kconfig.bflb"
|
||||
source "drivers/input/Kconfig.cap12xx"
|
||||
source "drivers/input/Kconfig.cf1133"
|
||||
source "drivers/input/Kconfig.ch9350l"
|
||||
source "drivers/input/Kconfig.chsc5x"
|
||||
source "drivers/input/Kconfig.chsc6x"
|
||||
source "drivers/input/Kconfig.cst816s"
|
||||
|
||||
23
drivers/input/Kconfig.ch9350l
Normal file
23
drivers/input/Kconfig.ch9350l
Normal file
@@ -0,0 +1,23 @@
|
||||
# Copyright (c) 2026 MASSDRIVER EI (massdriver.space)
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config INPUT_CH9350L
|
||||
bool "WinChipHead CH9350L USB HID to UART control chip"
|
||||
default y
|
||||
depends on DT_HAS_WCH_CH9350L_ENABLED
|
||||
depends on SERIAL_SUPPORT_INTERRUPT
|
||||
select UART
|
||||
select UART_INTERRUPT_DRIVEN
|
||||
help
|
||||
This option enables the driver for WinChipHead CH9350L USB HID to UART control chips.
|
||||
|
||||
if INPUT_CH9350L
|
||||
|
||||
config INPUT_CH9350L_FRAME_COUNT
|
||||
int "Number of frames allocatable"
|
||||
range 1 128
|
||||
default 2
|
||||
help
|
||||
Number of input frames queueable at once.
|
||||
|
||||
endif # INPUT_CH9350L
|
||||
414
drivers/input/input_ch9350l.c
Normal file
414
drivers/input/input_ch9350l.c
Normal file
@@ -0,0 +1,414 @@
|
||||
/*
|
||||
* Copyright (c) 2026 MASSDRIVER EI (massdriver.space)
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT wch_ch9350l
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/input/input.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/uart.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(input_ch9350l, CONFIG_INPUT_LOG_LEVEL);
|
||||
|
||||
/* The theorical maximum is 72 */
|
||||
#define CH9350L_FRAME_SIZE_MAX 72
|
||||
#define CH9350L_FRAME_SIZE_MIN 8
|
||||
#define CH9350L_WAIT_TIMEOUT_MS 100
|
||||
|
||||
#define CH9350L_READBUF_SIZE 16
|
||||
|
||||
#define CH9350L_FRAME_HEAD_0 0x57
|
||||
#define CH9350L_FRAME_HEAD_1 0xAB
|
||||
#define CH9350L_FRAME_HEAD_KEY_0 0x83
|
||||
#define CH9350L_FRAME_HEAD_KEY_1 0x88
|
||||
#define CH9350L_FRAME_HEAD_OFF 3
|
||||
#define CH9350L_FRAME_LENGTH_OFF 4
|
||||
|
||||
#define CH9350L_FRAME_VALUE_MAX (CH9350L_FRAME_SIZE_MAX - CH9350L_FRAME_HEAD_OFF - 4)
|
||||
|
||||
#define CH9350L_FRAME_TYPE_MASK GENMASK(5, 4)
|
||||
#define CH9350L_FRAME_TYPE_POS 4
|
||||
#define CH9350L_FRAME_TYPE_OTHER 0
|
||||
#define CH9350L_FRAME_TYPE_KB 1
|
||||
#define CH9350L_FRAME_TYPE_MOUSE 2
|
||||
#define CH9350L_FRAME_TYPE_MM 3
|
||||
|
||||
#define CH9350L_FRAME_MOUSE_BUTTON_BYTE 0
|
||||
#define CH9350L_FRAME_MOUSE_X_BYTE 1
|
||||
#define CH9350L_FRAME_MOUSE_Y_BYTE 3
|
||||
#define CH9350L_FRAME_MOUSE_RELMID 0x7FFF
|
||||
#define CH9350L_FRAME_MOUSE_RELNEG 0x8000
|
||||
|
||||
#define CH9350L_RAWMOUSE_TO_REL(_val) (_val > CH9350L_FRAME_MOUSE_RELMID ? \
|
||||
-(CH9350L_FRAME_MOUSE_RELMID - (const int16_t)(_val - CH9350L_FRAME_MOUSE_RELNEG)) \
|
||||
: (const int16_t)_val)
|
||||
|
||||
#define CH9350L_FRAME_MOUSE_BTN_LEFT 0x1
|
||||
#define CH9350L_FRAME_MOUSE_BTN_RIGHT 0x2
|
||||
#define CH9350L_FRAME_MOUSE_BTN_MID 0x4
|
||||
#define CH9350L_FRAME_MOUSE_BTN_4 0x8
|
||||
#define CH9350L_FRAME_MOUSE_BTN_5 0x10
|
||||
#define CH9350L_FRAME_MOUSE_BTN_6 0x20
|
||||
#define CH9350L_FRAME_MOUSE_BTN_7 0x40
|
||||
#define CH9350L_FRAME_MOUSE_BTN_8 0x80
|
||||
|
||||
static const uint8_t ch9350l_enable_status_frame[] = {
|
||||
0x57, 0xab, 0x12, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x20
|
||||
};
|
||||
static const uint8_t ch9350l_disable_status_frame[] = {
|
||||
0x57, 0xab, 0x12, 0x00, 0x00, 0x00, 0x00, 0xff, 0x80, 0x00, 0x20
|
||||
};
|
||||
static const uint8_t ch9350l_valid_start_of_status_frame[] = { 0x57, 0xab, 0x82 };
|
||||
|
||||
struct ch9350l_frame {
|
||||
uint8_t data[CH9350L_FRAME_SIZE_MAX - CH9350L_FRAME_HEAD_OFF];
|
||||
size_t size;
|
||||
};
|
||||
|
||||
#define CH9350L_MSGQBUF_SIZE (CONFIG_INPUT_CH9350L_FRAME_COUNT * sizeof(struct ch9350l_frame))
|
||||
#define CH9350L_MSGQBUF_TYPE char __aligned(4)
|
||||
|
||||
struct ch9350l_data {
|
||||
uint8_t frame_buffer[CH9350L_FRAME_SIZE_MAX];
|
||||
size_t frame_buffer_size;
|
||||
bool frame_started;
|
||||
uint8_t last_kb_values[CH9350L_FRAME_VALUE_MAX];
|
||||
uint8_t last_mouse_btns;
|
||||
const struct device *dev;
|
||||
struct k_work work;
|
||||
struct k_msgq msgq;
|
||||
CH9350L_MSGQBUF_TYPE msgq_buffer[CH9350L_MSGQBUF_SIZE];
|
||||
};
|
||||
|
||||
struct ch9350l_config {
|
||||
const struct device *uart;
|
||||
const int *kb_codemap;
|
||||
const size_t kb_codemap_len;
|
||||
const int *mouse_codemap;
|
||||
const size_t mouse_codemap_len;
|
||||
};
|
||||
|
||||
static uint32_t ch9350l_kb_map(const struct device *dev, uint32_t code)
|
||||
{
|
||||
const struct ch9350l_config *config = dev->config;
|
||||
const size_t code_map_len = config->kb_codemap_len / 2;
|
||||
|
||||
for (size_t i = 0; i < code_map_len; i++) {
|
||||
if (config->kb_codemap[i * 2] == code) {
|
||||
return config->kb_codemap[i * 2 + 1];
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
static void ch9350l_kb(const struct device *dev, const uint8_t *values, uint8_t length)
|
||||
{
|
||||
struct ch9350l_data *data = dev->data;
|
||||
uint8_t *lkbv = data->last_kb_values;
|
||||
bool found;
|
||||
|
||||
for (size_t j = 0; j < CH9350L_FRAME_VALUE_MAX; j++) {
|
||||
found = false;
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
if (lkbv[j] == values[i]) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found && lkbv[j] != 0) {
|
||||
if (input_report(dev, INPUT_EV_KEY,
|
||||
ch9350l_kb_map(dev, lkbv[j]), 0, true, K_FOREVER)) {
|
||||
LOG_ERR("Input failed to be enqueued");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
found = false;
|
||||
for (size_t j = 0; j < CH9350L_FRAME_VALUE_MAX; j++) {
|
||||
if (lkbv[j] == values[i]) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found && values[i] != 0) {
|
||||
if (input_report(dev, INPUT_EV_KEY,
|
||||
ch9350l_kb_map(dev, values[i]), 1, true, K_FOREVER)) {
|
||||
LOG_ERR("Input failed to be enqueued");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(lkbv, values, length);
|
||||
memset(&lkbv[length], 0, CH9350L_FRAME_VALUE_MAX - length);
|
||||
}
|
||||
|
||||
static uint32_t ch9350l_mouse_map(const struct device *dev, uint8_t code)
|
||||
{
|
||||
const struct ch9350l_config *config = dev->config;
|
||||
const size_t code_map_len = config->mouse_codemap_len / 2;
|
||||
|
||||
for (size_t i = 0; i < code_map_len; i++) {
|
||||
if (config->mouse_codemap[i * 2] == code) {
|
||||
return config->mouse_codemap[i * 2 + 1];
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
static void ch9350l_mouse(const struct device *dev, const uint8_t *values, uint8_t length)
|
||||
{
|
||||
struct ch9350l_data *data = dev->data;
|
||||
const uint8_t button = values[CH9350L_FRAME_MOUSE_BUTTON_BYTE];
|
||||
const uint16_t raw_x = sys_get_le16(&values[CH9350L_FRAME_MOUSE_X_BYTE]);
|
||||
const uint16_t raw_y = sys_get_le16(&values[CH9350L_FRAME_MOUSE_Y_BYTE]);
|
||||
const int16_t x = CH9350L_RAWMOUSE_TO_REL(raw_x);
|
||||
const int16_t y = CH9350L_RAWMOUSE_TO_REL(raw_y);
|
||||
|
||||
input_report(dev, INPUT_EV_REL, INPUT_REL_X, x, true, K_FOREVER);
|
||||
input_report(dev, INPUT_EV_REL, INPUT_REL_Y, y, true, K_FOREVER);
|
||||
|
||||
for (size_t i = 0; i < 8; i++) {
|
||||
if (button & BIT(i) && !(data->last_mouse_btns & BIT(i))) {
|
||||
if (input_report(dev, INPUT_EV_DEVICE,
|
||||
ch9350l_mouse_map(dev, BIT(i)), 1, true, K_FOREVER)) {
|
||||
LOG_ERR("Input failed to be enqueued");
|
||||
}
|
||||
} else if (data->last_mouse_btns & BIT(i) && !(button & BIT(i))) {
|
||||
if (input_report(dev, INPUT_EV_DEVICE,
|
||||
ch9350l_mouse_map(dev, BIT(i)), 0, true, K_FOREVER)) {
|
||||
LOG_ERR("Input failed to be enqueued");
|
||||
}
|
||||
} else {
|
||||
/* Dont care about other cases */
|
||||
}
|
||||
}
|
||||
data->last_mouse_btns = button;
|
||||
}
|
||||
|
||||
static void ch9350l_input_work_handler(struct k_work *item)
|
||||
{
|
||||
struct ch9350l_data *data = CONTAINER_OF(item, struct ch9350l_data, work);
|
||||
struct ch9350l_frame frame;
|
||||
uint8_t fd_label;
|
||||
uint8_t *fd_value;
|
||||
uint8_t fd_sum;
|
||||
uint8_t sum;
|
||||
|
||||
while (k_msgq_get(&data->msgq, &frame, K_NO_WAIT) == 0) {
|
||||
sum = 0;
|
||||
fd_label = frame.data[0];
|
||||
fd_value = &frame.data[1];
|
||||
fd_sum = frame.data[frame.size - 1];
|
||||
|
||||
for (int i = 0; i < frame.size - 2; i++) {
|
||||
sum += fd_value[i];
|
||||
}
|
||||
if (sum != fd_sum) {
|
||||
LOG_ERR("Frame checksum is invalid");
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ((fd_label & CH9350L_FRAME_TYPE_MASK) >> CH9350L_FRAME_TYPE_POS) {
|
||||
case CH9350L_FRAME_TYPE_KB:
|
||||
ch9350l_kb(data->dev, fd_value,
|
||||
min(frame.size - 3, CH9350L_FRAME_VALUE_MAX));
|
||||
continue;
|
||||
case CH9350L_FRAME_TYPE_MOUSE:
|
||||
ch9350l_mouse(data->dev, fd_value,
|
||||
min(frame.size - 3, CH9350L_FRAME_VALUE_MAX));
|
||||
continue;
|
||||
case CH9350L_FRAME_TYPE_MM:
|
||||
default:
|
||||
LOG_ERR("Unknown or unsupported input type");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int ch9350l_queue_frame(struct ch9350l_data *dev_data, uint8_t *data, size_t size)
|
||||
{
|
||||
int ret;
|
||||
struct ch9350l_frame frame = {
|
||||
.size = size,
|
||||
};
|
||||
memcpy(frame.data, data, size);
|
||||
|
||||
ret = k_msgq_put(&dev_data->msgq, &frame, K_NO_WAIT);
|
||||
if (ret < 0) {
|
||||
LOG_WRN("Frame dropped, queue full");
|
||||
}
|
||||
|
||||
k_work_submit(&dev_data->work);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint8_t ch9350l_is_valid_frame(uint8_t *data, size_t size)
|
||||
{
|
||||
const uint8_t fd_id = data[2];
|
||||
const uint8_t fd_length = data[3];
|
||||
|
||||
/* Too small to be valid */
|
||||
if (size < CH9350L_FRAME_SIZE_MIN) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Drop non-input frames */
|
||||
if (fd_id != CH9350L_FRAME_HEAD_KEY_0 && fd_id != CH9350L_FRAME_HEAD_KEY_1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We don't have the full frame yet */
|
||||
if (fd_length > (size - CH9350L_FRAME_LENGTH_OFF)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fd_length;
|
||||
}
|
||||
|
||||
static void ch9350l_input_callback(const struct device *dev_uart, void *user_data)
|
||||
{
|
||||
struct ch9350l_data *data = (struct ch9350l_data *)user_data;
|
||||
uint8_t read_buffer[CH9350L_READBUF_SIZE];
|
||||
int read;
|
||||
uint8_t frame_size;
|
||||
|
||||
uart_irq_update(dev_uart);
|
||||
if (!uart_irq_rx_ready(dev_uart)) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
read = uart_fifo_read(dev_uart, read_buffer, CH9350L_READBUF_SIZE);
|
||||
if (read <= 0) {
|
||||
break;
|
||||
}
|
||||
if (data->frame_buffer_size + read >= CH9350L_FRAME_SIZE_MAX) {
|
||||
LOG_ERR("Maximum frame size exceeded");
|
||||
data->frame_started = false;
|
||||
data->frame_buffer_size = 0;
|
||||
continue;
|
||||
}
|
||||
memcpy(&data->frame_buffer[data->frame_buffer_size], read_buffer, read);
|
||||
data->frame_buffer_size += read;
|
||||
for (size_t offset = max((int)data->frame_buffer_size - read - 2, 0);
|
||||
offset < (data->frame_buffer_size - 1); offset++) {
|
||||
if (data->frame_buffer[offset] != CH9350L_FRAME_HEAD_0
|
||||
|| data->frame_buffer[offset+1] != CH9350L_FRAME_HEAD_1) {
|
||||
continue;
|
||||
}
|
||||
if (data->frame_started) {
|
||||
frame_size = ch9350l_is_valid_frame(data->frame_buffer,
|
||||
data->frame_buffer_size);
|
||||
if (frame_size) {
|
||||
ch9350l_queue_frame(data,
|
||||
&data->frame_buffer[CH9350L_FRAME_LENGTH_OFF],
|
||||
frame_size);
|
||||
}
|
||||
}
|
||||
data->frame_started = true;
|
||||
memcpy(data->frame_buffer, &data->frame_buffer[offset],
|
||||
data->frame_buffer_size - offset);
|
||||
data->frame_buffer_size = data->frame_buffer_size - offset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (data->frame_started) {
|
||||
frame_size = ch9350l_is_valid_frame(data->frame_buffer, data->frame_buffer_size);
|
||||
if (frame_size) {
|
||||
ch9350l_queue_frame(data, &data->frame_buffer[CH9350L_FRAME_LENGTH_OFF],
|
||||
frame_size);
|
||||
data->frame_started = false;
|
||||
data->frame_buffer_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (read < 0) {
|
||||
LOG_ERR("Error reading UART");
|
||||
}
|
||||
}
|
||||
|
||||
static int ch9350l_init(struct device const *dev)
|
||||
{
|
||||
const struct ch9350l_config *config = dev->config;
|
||||
struct ch9350l_data *data = dev->data;
|
||||
k_timepoint_t end_timeout =
|
||||
sys_timepoint_calc(K_MSEC(CH9350L_WAIT_TIMEOUT_MS));
|
||||
size_t check_p = 0;
|
||||
int ret = 0;
|
||||
char c;
|
||||
|
||||
data->dev = dev;
|
||||
data->frame_buffer_size = 0;
|
||||
data->frame_started = false;
|
||||
k_work_init(&data->work, ch9350l_input_work_handler);
|
||||
k_msgq_init(&data->msgq, data->msgq_buffer, sizeof(struct ch9350l_frame),
|
||||
CONFIG_INPUT_CH9350L_FRAME_COUNT);
|
||||
|
||||
if (!device_is_ready(config->uart)) {
|
||||
LOG_ERR("UART device not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
for (int i = 0; i < sizeof(ch9350l_enable_status_frame); i++) {
|
||||
uart_poll_out(config->uart, ch9350l_enable_status_frame[i]);
|
||||
}
|
||||
|
||||
while ((ret == 0 || ret == -1) && !sys_timepoint_expired(end_timeout)
|
||||
&& check_p < ARRAY_SIZE(ch9350l_valid_start_of_status_frame)) {
|
||||
ret = uart_poll_in(config->uart, &c);
|
||||
if (c == ch9350l_valid_start_of_status_frame[check_p]) {
|
||||
check_p++;
|
||||
}
|
||||
}
|
||||
if (check_p != ARRAY_SIZE(ch9350l_valid_start_of_status_frame)) {
|
||||
LOG_ERR("CH9350L not detected");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/* Flush FIFO if applicable */
|
||||
while (uart_poll_in(config->uart, &c)) {
|
||||
};
|
||||
|
||||
for (int i = 0; i < sizeof(ch9350l_disable_status_frame); i++) {
|
||||
uart_poll_out(config->uart, ch9350l_disable_status_frame[i]);
|
||||
}
|
||||
|
||||
ret = uart_irq_callback_user_data_set(config->uart, ch9350l_input_callback, data);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Couldn't set UART callback");
|
||||
return ret;
|
||||
}
|
||||
uart_irq_rx_enable(config->uart);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define CH9350L_DEFINE(inst) \
|
||||
static struct ch9350l_data ch9350l_data_##inst = {0}; \
|
||||
\
|
||||
static struct ch9350l_config const ch9350l_config_##inst = { \
|
||||
.uart = DEVICE_DT_GET(DT_INST_BUS(inst)), \
|
||||
.kb_codemap = COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(inst), kb_codemap), \
|
||||
((int []) DT_INST_PROP(inst, kb_codemap)), NULL), \
|
||||
.kb_codemap_len = DT_INST_PROP_LEN_OR(inst, kb_codemap, 0), \
|
||||
.mouse_codemap = COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(inst), mouse_codemap),\
|
||||
((int []) DT_INST_PROP(inst, mouse_codemap)), NULL), \
|
||||
.mouse_codemap_len = DT_INST_PROP_LEN_OR(inst, mouse_codemap, 0), \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(inst, ch9350l_init, NULL, &ch9350l_data_##inst, \
|
||||
&ch9350l_config_##inst, POST_KERNEL, CONFIG_INPUT_INIT_PRIORITY, \
|
||||
NULL); \
|
||||
BUILD_ASSERT((DT_INST_PROP_LEN_OR(inst, kb_code_map, 0) & 0x1) == 0, \
|
||||
"kb-codemap is not of a valid size"); \
|
||||
BUILD_ASSERT((DT_INST_PROP_LEN_OR(inst, mouse_code_map, 0) & 0x1) == 0, \
|
||||
"mouse-codemap is not of a valid size");
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(CH9350L_DEFINE)
|
||||
42
dts/bindings/input/wch,ch9350l.yaml
Normal file
42
dts/bindings/input/wch,ch9350l.yaml
Normal file
@@ -0,0 +1,42 @@
|
||||
# Copyright (c) 2026 MASSDRIVER EI (massdriver.space)
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: |
|
||||
WinChipHead CH9350L USB HID to UART control chip.
|
||||
This supports State 0.
|
||||
|
||||
|
||||
&uart0 {
|
||||
status = "okay";
|
||||
|
||||
usb_kb {
|
||||
compatible = "wch,ch9350l";
|
||||
status = "okay";
|
||||
|
||||
kb-codemap = <
|
||||
4 INPUT_KEY_Q
|
||||
5 INPUT_KEY_B
|
||||
6 INPUT_KEY_C
|
||||
>;
|
||||
mouse-codemap = <
|
||||
1 INPUT_BTN_LEFT
|
||||
2 INPUT_BTN_RIGHT
|
||||
4 INPUT_BTN_MIDDLE
|
||||
>;
|
||||
};
|
||||
};
|
||||
|
||||
compatible: "wch,ch9350l"
|
||||
|
||||
include: [base.yaml, uart-device.yaml]
|
||||
|
||||
properties:
|
||||
kb-codemap:
|
||||
type: array
|
||||
description: |
|
||||
Maps a keyboard key code to a input code by pair of 2 when needed.
|
||||
|
||||
mouse-codemap:
|
||||
type: array
|
||||
description: |
|
||||
Maps a mouse button code to a input code by pair of 2 when needed.
|
||||
Reference in New Issue
Block a user