This commit is contained in:
Adrien Friggeri 2024-03-25 11:12:48 +08:00 committed by GitHub
commit f859d68b61
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 220 additions and 1 deletions

View file

@ -213,6 +213,10 @@ config ZMK_BLE_MOUSE_REPORT_QUEUE_SIZE
int "Max number of mouse HID reports to queue for sending over BLE" int "Max number of mouse HID reports to queue for sending over BLE"
default 20 default 20
config ZMK_BLE_JOYSTICK_REPORT_QUEUE_SIZE
int "Max number of joystick HID reports to queue for sending over BLE"
default 20
config ZMK_BLE_CLEAR_BONDS_ON_START config ZMK_BLE_CLEAR_BONDS_ON_START
bool "Configuration that clears all bond information from the keyboard on startup." bool "Configuration that clears all bond information from the keyboard on startup."
@ -375,6 +379,14 @@ config ZMK_MOUSE
#Mouse Options #Mouse Options
endmenu endmenu
menu "Joystick Options"
config ZMK_JOYSTICK
bool "Enable ZMK joystick emulation"
#Joystick Options
endmenu
menu "Power Management" menu "Power Management"
config ZMK_BATTERY_REPORTING config ZMK_BATTERY_REPORTING

View file

@ -73,3 +73,7 @@ int zmk_endpoints_send_report(uint16_t usage_page);
#if IS_ENABLED(CONFIG_ZMK_MOUSE) #if IS_ENABLED(CONFIG_ZMK_MOUSE)
int zmk_endpoints_send_mouse_report(); int zmk_endpoints_send_mouse_report();
#endif // IS_ENABLE(CONFIG_ZMK_MOUSE) #endif // IS_ENABLE(CONFIG_ZMK_MOUSE)
#if IS_ENABLED(CONFIG_ZMK_JOYSTICK)
int zmk_endpoints_send_joystick_report();
#endif // IS_ENABLE(CONFIG_ZMK_JOYSTICK)

View file

@ -58,6 +58,7 @@
#define ZMK_HID_REPORT_ID_LEDS 0x01 #define ZMK_HID_REPORT_ID_LEDS 0x01
#define ZMK_HID_REPORT_ID_CONSUMER 0x02 #define ZMK_HID_REPORT_ID_CONSUMER 0x02
#define ZMK_HID_REPORT_ID_MOUSE 0x03 #define ZMK_HID_REPORT_ID_MOUSE 0x03
#define ZMK_HID_REPORT_ID_JOYSTICK 0x04
static const uint8_t zmk_hid_report_desc[] = { static const uint8_t zmk_hid_report_desc[] = {
HID_USAGE_PAGE(HID_USAGE_GEN_DESKTOP), HID_USAGE_PAGE(HID_USAGE_GEN_DESKTOP),
@ -175,6 +176,23 @@ static const uint8_t zmk_hid_report_desc[] = {
HID_END_COLLECTION, HID_END_COLLECTION,
HID_END_COLLECTION, HID_END_COLLECTION,
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) #endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
#if IS_ENABLED(CONFIG_ZMK_JOYSTICK)
HID_USAGE_PAGE(HID_USAGE_GD),
HID_USAGE(HID_USAGE_GD_JOYSTICK),
HID_COLLECTION(HID_COLLECTION_APPLICATION),
HID_REPORT_ID(ZMK_HID_REPORT_ID_JOYSTICK),
HID_COLLECTION(HID_COLLECTION_LOGICAL),
HID_USAGE(HID_USAGE_GD_X),
HID_USAGE(HID_USAGE_GD_Y),
HID_USAGE(HID_USAGE_GD_Z),
HID_LOGICAL_MIN8(-0x7F),
HID_LOGICAL_MAX8(0x7F),
HID_REPORT_SIZE(0x08),
HID_REPORT_COUNT(0x03),
HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_ABS),
HID_END_COLLECTION,
HID_END_COLLECTION,
#endif // IS_ENABLED(CONFIG_ZMK_JOYSTICK)
}; };
#if IS_ENABLED(CONFIG_ZMK_USB_BOOT) #if IS_ENABLED(CONFIG_ZMK_USB_BOOT)
@ -252,6 +270,19 @@ struct zmk_hid_mouse_report {
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) #endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
#if IS_ENABLED(CONFIG_ZMK_JOYSTICK)
struct zmk_hid_joystick_report_body {
int8_t x;
int8_t y;
int8_t z;
} __packed;
struct zmk_hid_joystick_report {
uint8_t report_id;
struct zmk_hid_joystick_report_body body;
} __packed;
#endif // IS_ENABLED(CONFIG_ZMK_JOYSTICK)
zmk_mod_flags_t zmk_hid_get_explicit_mods(void); zmk_mod_flags_t zmk_hid_get_explicit_mods(void);
int zmk_hid_register_mod(zmk_mod_t modifier); int zmk_hid_register_mod(zmk_mod_t modifier);
int zmk_hid_unregister_mod(zmk_mod_t modifier); int zmk_hid_unregister_mod(zmk_mod_t modifier);
@ -286,6 +317,11 @@ int zmk_hid_mouse_buttons_release(zmk_mouse_button_flags_t buttons);
void zmk_hid_mouse_clear(void); void zmk_hid_mouse_clear(void);
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) #endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
#if IS_ENABLED(CONFIG_ZMK_JOYSTICK)
void zmk_hid_joystick_set(uint8_t x, uint8_t y, uint8_t z);
void zmk_hid_joystick_clear(void);
#endif // IS_ENABLED(CONFIG_ZMK_JOYSTICK)
struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report(void); struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report(void);
struct zmk_hid_consumer_report *zmk_hid_get_consumer_report(void); struct zmk_hid_consumer_report *zmk_hid_get_consumer_report(void);
@ -296,3 +332,7 @@ zmk_hid_boot_report_t *zmk_hid_get_boot_report();
#if IS_ENABLED(CONFIG_ZMK_MOUSE) #if IS_ENABLED(CONFIG_ZMK_MOUSE)
struct zmk_hid_mouse_report *zmk_hid_get_mouse_report(); struct zmk_hid_mouse_report *zmk_hid_get_mouse_report();
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) #endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
#if IS_ENABLED(CONFIG_ZMK_JOYSTICK)
struct zmk_hid_joystick_report *zmk_hid_get_joystick_report();
#endif // IS_ENABLED(CONFIG_ZMK_JOYSTICK)

View file

@ -15,3 +15,7 @@ int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *body);
#if IS_ENABLED(CONFIG_ZMK_MOUSE) #if IS_ENABLED(CONFIG_ZMK_MOUSE)
int zmk_hog_send_mouse_report(struct zmk_hid_mouse_report_body *body); int zmk_hog_send_mouse_report(struct zmk_hid_mouse_report_body *body);
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) #endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
#if IS_ENABLED(CONFIG_ZMK_JOYSTICK)
int zmk_hog_send_joystick_report(struct zmk_hid_joystick_report_body *body);
#endif // IS_ENABLED(CONFIG_ZMK_JOYSTICK)

View file

@ -13,4 +13,7 @@ int zmk_usb_hid_send_consumer_report(void);
#if IS_ENABLED(CONFIG_ZMK_MOUSE) #if IS_ENABLED(CONFIG_ZMK_MOUSE)
int zmk_usb_hid_send_mouse_report(void); int zmk_usb_hid_send_mouse_report(void);
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) #endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
#if IS_ENABLED(CONFIG_ZMK_JOYSTICK)
int zmk_usb_hid_send_joystick_report(void);
#endif // IS_ENABLED(CONFIG_ZMK_JOYSTICK)
void zmk_usb_hid_set_protocol(uint8_t protocol); void zmk_usb_hid_set_protocol(uint8_t protocol);

View file

@ -134,7 +134,7 @@ static int bvd_init(const struct device *dev) {
.channels = BIT(0), .channels = BIT(0),
.buffer = &drv_data->value.adc_raw, .buffer = &drv_data->value.adc_raw,
.buffer_size = sizeof(drv_data->value.adc_raw), .buffer_size = sizeof(drv_data->value.adc_raw),
.oversampling = 4, .oversampling = 0,
.calibrate = true, .calibrate = true,
}; };

View file

@ -239,6 +239,42 @@ int zmk_endpoints_send_mouse_report() {
} }
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) #endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
#if IS_ENABLED(CONFIG_ZMK_JOYSTICK)
int zmk_endpoints_send_joystick_report() {
switch (current_instance.transport) {
case ZMK_TRANSPORT_USB: {
#if IS_ENABLED(CONFIG_ZMK_USB)
int err = zmk_usb_hid_send_joystick_report();
if (err) {
LOG_ERR("FAILED TO SEND OVER USB: %d", err);
}
return err;
#else
LOG_ERR("USB endpoint is not supported");
return -ENOTSUP;
#endif /* IS_ENABLED(CONFIG_ZMK_USB) */
}
case ZMK_TRANSPORT_BLE: {
#if IS_ENABLED(CONFIG_ZMK_BLE)
struct zmk_hid_joystick_report *joystick_report = zmk_hid_get_joystick_report();
int err = zmk_hog_send_joystick_report(&joystick_report->body);
if (err) {
LOG_ERR("FAILED TO SEND OVER HOG: %d", err);
}
return err;
#else
LOG_ERR("BLE HOG endpoint is not supported");
return -ENOTSUP;
#endif /* IS_ENABLED(CONFIG_ZMK_BLE) */
}
}
LOG_ERR("Unhandled endpoint transport %d", current_instance.transport);
return -ENOTSUP;
}
#endif // IS_ENABLED(CONFIG_ZMK_JOYSTICK)
#if IS_ENABLED(CONFIG_SETTINGS) #if IS_ENABLED(CONFIG_SETTINGS)
static int endpoints_handle_set(const char *name, size_t len, settings_read_cb read_cb, static int endpoints_handle_set(const char *name, size_t len, settings_read_cb read_cb,

View file

@ -32,6 +32,13 @@ static struct zmk_hid_mouse_report mouse_report = {.report_id = ZMK_HID_REPORT_I
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) #endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
#if IS_ENABLED(CONFIG_ZMK_JOYSTICK)
static struct zmk_hid_joystick_report joystick_report = {.report_id = ZMK_HID_REPORT_ID_JOYSTICK,
.body = {.x = 0, .y = 0, .z = 0}};
#endif // IS_ENABLED(CONFIG_ZMK_JOYSTICK)
// Keep track of how often a modifier was pressed. // Keep track of how often a modifier was pressed.
// Only release the modifier if the count is 0. // Only release the modifier if the count is 0.
static int explicit_modifier_counts[8] = {0, 0, 0, 0, 0, 0, 0, 0}; static int explicit_modifier_counts[8] = {0, 0, 0, 0, 0, 0, 0, 0};
@ -434,6 +441,18 @@ void zmk_hid_mouse_clear(void) { memset(&mouse_report.body, 0, sizeof(mouse_repo
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) #endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
#if IS_ENABLED(CONFIG_ZMK_JOYSTICK)
void zmk_hid_joystick_set(uint8_t x, uint8_t y, uint8_t z) {
joystick_report.body.x = x;
joystick_report.body.y = y;
joystick_report.body.z = z;
};
void zmk_hid_joystick_clear(void) {
memset(&joystick_report.body, 0, sizeof(joystick_report.body));
}
#endif // IS_ENABLED(CONFIG_ZMK_JOYSTICK)
struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report(void) { struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report(void) {
return &keyboard_report; return &keyboard_report;
} }
@ -449,3 +468,9 @@ struct zmk_hid_mouse_report *zmk_hid_get_mouse_report(void) {
} }
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) #endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
#if IS_ENABLED(CONFIG_ZMK_JOYSTICK)
struct zmk_hid_joystick_report *zmk_hid_get_joystick_report(void) {
return &joystick_report;
};
#endif // IS_ENABLED(CONFIG_ZMK_JOYSTICK)

View file

@ -78,6 +78,15 @@ static struct hids_report mouse_input = {
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) #endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
#if IS_ENABLED(CONFIG_ZMK_JOYSTICK)
static struct hids_report joystick_input = {
.id = ZMK_HID_REPORT_ID_JOYSTICK,
.type = HIDS_INPUT,
};
#endif // IS_ENABLED(CONFIG_ZMK_JOYSTICK)
static bool host_requests_notification = false; static bool host_requests_notification = false;
static uint8_t ctrl_point; static uint8_t ctrl_point;
// static uint8_t proto_mode; // static uint8_t proto_mode;
@ -152,6 +161,16 @@ static ssize_t read_hids_mouse_input_report(struct bt_conn *conn, const struct b
} }
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) #endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
#if IS_ENABLED(CONFIG_ZMK_JOYSTICK)
static ssize_t read_hids_joystick_input_report(struct bt_conn *conn,
const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset) {
struct zmk_hid_joystick_report_body *report_body = &zmk_hid_get_joystick_report()->body;
return bt_gatt_attr_read(conn, attr, buf, len, offset, report_body,
sizeof(struct zmk_hid_joystick_report_body));
}
#endif // IS_ENABLED(CONFIG_ZMK_JOYSTICK)
// static ssize_t write_proto_mode(struct bt_conn *conn, // static ssize_t write_proto_mode(struct bt_conn *conn,
// const struct bt_gatt_attr *attr, // const struct bt_gatt_attr *attr,
// const void *buf, uint16_t len, uint16_t offset, // const void *buf, uint16_t len, uint16_t offset,
@ -208,6 +227,14 @@ BT_GATT_SERVICE_DEFINE(
NULL, &mouse_input), NULL, &mouse_input),
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) #endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
#if IS_ENABLED(CONFIG_ZMK_JOYSTICK)
BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_READ_ENCRYPT, read_hids_joystick_input_report, NULL, NULL),
BT_GATT_CCC(input_ccc_changed, BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT),
BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ_ENCRYPT, read_hids_report_ref,
NULL, &joystick_input),
#endif // IS_ENABLED(CONFIG_ZMK_JOYSTICK)
#if IS_ENABLED(CONFIG_ZMK_HID_INDICATORS) #if IS_ENABLED(CONFIG_ZMK_HID_INDICATORS)
BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT, BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT,
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP, BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP,
@ -398,6 +425,61 @@ int zmk_hog_send_mouse_report(struct zmk_hid_mouse_report_body *report) {
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) #endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
#if IS_ENABLED(CONFIG_ZMK_JOYSTICK)
K_MSGQ_DEFINE(zmk_hog_joystick_msgq, sizeof(struct zmk_hid_joystick_report_body),
CONFIG_ZMK_BLE_JOYSTICK_REPORT_QUEUE_SIZE, 4);
void send_joystick_report_callback(struct k_work *work) {
struct zmk_hid_joystick_report_body report;
while (k_msgq_get(&zmk_hog_joystick_msgq, &report, K_NO_WAIT) == 0) {
struct bt_conn *conn = destination_connection();
if (conn == NULL) {
return;
}
struct bt_gatt_notify_params notify_params = {
.attr = &hog_svc.attrs[13],
.data = &report,
.len = sizeof(report),
};
int err = bt_gatt_notify_cb(conn, &notify_params);
if (err == -EPERM) {
bt_conn_set_security(conn, BT_SECURITY_L2);
} else if (err) {
LOG_DBG("Error notifying %d", err);
}
bt_conn_unref(conn);
}
};
K_WORK_DEFINE(hog_joystick_work, send_joystick_report_callback);
int zmk_hog_send_joystick_report(struct zmk_hid_joystick_report_body *report) {
int err = k_msgq_put(&zmk_hog_joystick_msgq, report, K_MSEC(100));
if (err) {
switch (err) {
case -EAGAIN: {
LOG_WRN("Consumer message queue full, popping first message and queueing again");
struct zmk_hid_joystick_report_body discarded_report;
k_msgq_get(&zmk_hog_joystick_msgq, &discarded_report, K_NO_WAIT);
return zmk_hog_send_joystick_report(report);
}
default:
LOG_WRN("Failed to queue joystick report to send (%d)", err);
return err;
}
}
k_work_submit_to_queue(&hog_work_q, &hog_joystick_work);
return 0;
};
#endif // IS_ENABLED(CONFIG_ZMK_JOYSTICK)
static int zmk_hog_init(void) { static int zmk_hog_init(void) {
static const struct k_work_queue_config queue_config = {.name = "HID Over GATT Send Work"}; static const struct k_work_queue_config queue_config = {.name = "HID Over GATT Send Work"};
k_work_queue_start(&hog_work_q, hog_q_stack, K_THREAD_STACK_SIZEOF(hog_q_stack), k_work_queue_start(&hog_work_q, hog_q_stack, K_THREAD_STACK_SIZEOF(hog_q_stack),

View file

@ -177,6 +177,19 @@ int zmk_usb_hid_send_mouse_report() {
} }
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE) #endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
#if IS_ENABLED(CONFIG_ZMK_JOYSTICK)
int zmk_usb_hid_send_joystick_report() {
#if IS_ENABLED(CONFIG_ZMK_USB_BOOT)
if (hid_protocol == HID_PROTOCOL_BOOT) {
return -ENOTSUP;
}
#endif /* IS_ENABLED(CONFIG_ZMK_USB_BOOT) */
struct zmk_hid_joystick_report *report = zmk_hid_get_joystick_report();
return zmk_usb_hid_send_report((uint8_t *)report, sizeof(*report));
}
#endif // IS_ENABLED(CONFIG_ZMK_JOYSTICK)
static int zmk_usb_hid_init(void) { static int zmk_usb_hid_init(void) {
hid_dev = device_get_binding("HID_0"); hid_dev = device_get_binding("HID_0");
if (hid_dev == NULL) { if (hid_dev == NULL) {