/** @file * @brief HoG Service sample */ /* * Copyright (c) 2016 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include #include #include #include #include enum { HIDS_REMOTE_WAKE = BIT(0), HIDS_NORMALLY_CONNECTABLE = BIT(1), }; struct hids_info { uint16_t version; /* version number of base USB HID Specification */ uint8_t code; /* country HID Device hardware is localized for. */ uint8_t flags; } __packed; struct hids_report { uint8_t id; /* report id */ uint8_t type; /* report type */ } __packed; static struct hids_info info = { .version = 0x0000, .code = 0x00, .flags = HIDS_NORMALLY_CONNECTABLE, }; enum { HIDS_INPUT = 0x01, HIDS_OUTPUT = 0x02, HIDS_FEATURE = 0x03, }; static struct hids_report input = { .id = 0x01, .type = HIDS_INPUT, }; static uint8_t simulate_input; static uint8_t ctrl_point; static uint8_t report_map[] = { 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */ 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 (0x01) */ 0x29, 0x03, /* Usage Maximum (0x03) */ 0x15, 0x00, /* Logical Minimum (0) */ 0x25, 0x01, /* Logical Maximum (1) */ 0x95, 0x03, /* Report Count (3) */ 0x75, 0x01, /* Report Size (1) */ 0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,...) */ 0x95, 0x01, /* Report Count (1) */ 0x75, 0x05, /* Report Size (5) */ 0x81, 0x03, /* Input (Const,Var,Abs,No Wrap,Linear,...) */ 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */ 0x09, 0x30, /* Usage (X) */ 0x09, 0x31, /* Usage (Y) */ 0x15, 0x81, /* Logical Minimum (129) */ 0x25, 0x7F, /* Logical Maximum (127) */ 0x75, 0x08, /* Report Size (8) */ 0x95, 0x02, /* Report Count (2) */ 0x81, 0x06, /* Input (Data,Var,Rel,No Wrap,Linear,...) */ 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(struct 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(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(struct hids_report)); } static void input_ccc_changed(const struct bt_gatt_attr *attr, uint16_t value) { simulate_input = (value == BT_GATT_CCC_NOTIFY) ? 1 : 0; } 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_ctrl_point(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, uint16_t len, uint16_t offset, uint8_t flags) { uint8_t *value = attr->user_data; if (offset + len > sizeof(ctrl_point)) { return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); } memcpy(value + offset, buf, len); return len; } #if CONFIG_SAMPLE_BT_USE_AUTHENTICATION /* Require encryption using authenticated link-key. */ #define SAMPLE_BT_PERM_READ BT_GATT_PERM_READ_AUTHEN #define SAMPLE_BT_PERM_WRITE BT_GATT_PERM_WRITE_AUTHEN #else /* Require encryption. */ #define SAMPLE_BT_PERM_READ BT_GATT_PERM_READ_ENCRYPT #define SAMPLE_BT_PERM_WRITE BT_GATT_PERM_WRITE_ENCRYPT #endif /* HID Service Declaration */ 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, &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, SAMPLE_BT_PERM_READ, read_input_report, NULL, NULL), BT_GATT_CCC(input_ccc_changed, SAMPLE_BT_PERM_READ | SAMPLE_BT_PERM_WRITE), BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ, read_report, NULL, &input), BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_CTRL_POINT, BT_GATT_CHRC_WRITE_WITHOUT_RESP, BT_GATT_PERM_WRITE, NULL, write_ctrl_point, &ctrl_point), ); void hog_init(void) { } #define SW0_NODE DT_ALIAS(sw0) void hog_button_loop(void) { #if DT_NODE_HAS_STATUS(SW0_NODE, okay) const struct gpio_dt_spec sw0 = GPIO_DT_SPEC_GET(SW0_NODE, gpios); gpio_pin_configure_dt(&sw0, GPIO_INPUT); for (;;) { if (simulate_input) { /* HID Report: * Byte 0: buttons (lower 3 bits) * Byte 1: X axis (int8) * Byte 2: Y axis (int8) */ int8_t report[3] = {0, 0, 0}; if (gpio_pin_get_dt(&sw0)) { report[0] |= BIT(0); } bt_gatt_notify(NULL, &hog_svc.attrs[5], report, sizeof(report)); } k_sleep(K_MSEC(100)); } #endif }