MustafaMulla29/wireless-mouse-pcb
A tscircuit package created by MustafaMulla29.
- Version
- 1.0.16
- License
- unset
- Stars
- 0
firmware/src/hid_mouse.c
/* SPDX-License-Identifier: Apache-2.0 */
/* HID-over-GATT service structure follows Zephyr's official
* samples/bluetooth/peripheral_hids example.
*/
#include "hid_mouse.h"
#include <errno.h>
#include <stddef.h>
#include <string.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/settings/settings.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/util.h>
enum {
HIDS_REMOTE_WAKE = BIT(0),
HIDS_NORMALLY_CONNECTABLE = BIT(1),
};
enum {
HIDS_INPUT = 0x01,
};
struct hids_info {
uint16_t version;
uint8_t country_code;
uint8_t flags;
} __packed;
struct hids_report_ref {
uint8_t id;
uint8_t type;
} __packed;
struct mouse_report {
uint8_t buttons;
int16_t x;
int16_t y;
int8_t wheel;
} __packed;
static struct hids_info hids_info = {
.version = sys_cpu_to_le16(0x0111),
.country_code = 0,
.flags = HIDS_NORMALLY_CONNECTABLE,
};
static struct hids_report_ref input_report_ref = {
.id = 1,
.type = HIDS_INPUT,
};
static uint8_t control_point;
static bool notifications_enabled;
static struct bt_conn *active_conn;
static const uint8_t report_map[] = {
0x05, 0x01, /* Usage Page (Generic Desktop) */
0x09, 0x02, /* Usage (Mouse) */
0xA1, 0x01, /* Collection (Application) */
0x85, 0x01, /* Report ID (1) */
0x09, 0x01, /* Usage (Pointer) */
0xA1, 0x00, /* Collection (Physical) */
0x05, 0x09, /* Usage Page (Button) */
0x19, 0x01, /* Usage Minimum (1) */
0x29, 0x05, /* Usage Maximum (5) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x95, 0x05, /* Report Count (5) */
0x75, 0x01, /* Report Size (1) */
0x81, 0x02, /* Input (Data, Variable, Absolute) */
0x95, 0x01, /* Report Count (1) */
0x75, 0x03, /* Report Size (3) */
0x81, 0x03, /* Input (Constant) */
0x05, 0x01, /* Usage Page (Generic Desktop) */
0x09, 0x30, /* Usage (X) */
0x09, 0x31, /* Usage (Y) */
0x16, 0x01, 0x80, /* Logical Minimum (-32767) */
0x26, 0xFF, 0x7F, /* Logical Maximum (32767) */
0x75, 0x10, /* Report Size (16) */
0x95, 0x02, /* Report Count (2) */
0x81, 0x06, /* Input (Data, Variable, Relative) */
0x09, 0x38, /* Usage (Wheel) */
0x15, 0x81, /* Logical Minimum (-127) */
0x25, 0x7F, /* Logical Maximum (127) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x01, /* Report Count (1) */
0x81, 0x06, /* Input (Data, Variable, Relative) */
0xC0, /* End Collection */
0xC0, /* End Collection */
};
static ssize_t read_info(struct bt_conn *conn, const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
return bt_gatt_attr_read(conn, attr, buf, len, offset, attr->user_data,
sizeof(hids_info));
}
static ssize_t read_report_map(struct bt_conn *conn,
const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset)
{
return bt_gatt_attr_read(conn, attr, buf, len, offset, report_map,
sizeof(report_map));
}
static ssize_t read_report_ref(struct bt_conn *conn,
const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset)
{
return bt_gatt_attr_read(conn, attr, buf, len, offset, attr->user_data,
sizeof(input_report_ref));
}
static ssize_t read_input_report(struct bt_conn *conn,
const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset)
{
return bt_gatt_attr_read(conn, attr, buf, len, offset, NULL, 0);
}
static ssize_t write_control_point(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
const void *buf, uint16_t len,
uint16_t offset, uint8_t flags)
{
if (offset + len > sizeof(control_point)) {
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
}
memcpy((uint8_t *)attr->user_data + offset, buf, len);
return len;
}
static void input_ccc_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
notifications_enabled = (value == BT_GATT_CCC_NOTIFY);
}
BT_GATT_SERVICE_DEFINE(hog_svc,
BT_GATT_PRIMARY_SERVICE(BT_UUID_HIDS),
BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_INFO, BT_GATT_CHRC_READ,
BT_GATT_PERM_READ, read_info, NULL, &hids_info),
BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT_MAP, BT_GATT_CHRC_READ,
BT_GATT_PERM_READ, read_report_map, NULL, NULL),
BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT,
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_READ_ENCRYPT,
read_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,
read_report_ref, NULL, &input_report_ref),
BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_CTRL_POINT,
BT_GATT_CHRC_WRITE_WITHOUT_RESP,
BT_GATT_PERM_WRITE_ENCRYPT,
NULL, write_control_point, &control_point),
);
static const struct bt_data advertising_data[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR),
BT_DATA_BYTES(BT_DATA_UUID16_ALL,
BT_UUID_16_ENCODE(BT_UUID_HIDS_VAL),
BT_UUID_16_ENCODE(BT_UUID_BAS_VAL)),
};
static const struct bt_data scan_response[] = {
BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME,
sizeof(CONFIG_BT_DEVICE_NAME) - 1),
};
static int start_advertising(void)
{
return bt_le_adv_start(BT_LE_ADV_CONN_FAST_1,
advertising_data, ARRAY_SIZE(advertising_data),
scan_response, ARRAY_SIZE(scan_response));
}
static void connected(struct bt_conn *conn, uint8_t err)
{
if (err != 0) {
return;
}
active_conn = bt_conn_ref(conn);
(void)bt_conn_set_security(conn, BT_SECURITY_L2);
}
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
ARG_UNUSED(conn);
ARG_UNUSED(reason);
notifications_enabled = false;
if (active_conn != NULL) {
bt_conn_unref(active_conn);
active_conn = NULL;
}
(void)start_advertising();
}
BT_CONN_CB_DEFINE(connection_callbacks) = {
.connected = connected,
.disconnected = disconnected,
};
int hid_mouse_init(void)
{
int err = bt_enable(NULL);
if (err != 0) {
return err;
}
if (IS_ENABLED(CONFIG_SETTINGS)) {
err = settings_load();
if (err != 0) {
return err;
}
}
return start_advertising();
}
bool hid_mouse_is_connected(void)
{
return active_conn != NULL && notifications_enabled;
}
int hid_mouse_send(uint8_t buttons, int16_t x, int16_t y, int8_t wheel)
{
struct mouse_report report = {
.buttons = buttons & 0x1F,
.x = (int16_t)sys_cpu_to_le16((uint16_t)x),
.y = (int16_t)sys_cpu_to_le16((uint16_t)y),
.wheel = wheel,
};
if (!hid_mouse_is_connected()) {
return -ENOTCONN;
}
/* This is the characteristic declaration index used by Zephyr's
* official peripheral_hids sample; bt_gatt_notify resolves its value.
*/
return bt_gatt_notify(active_conn, &hog_svc.attrs[5], &report,
sizeof(report));
}