1 /** @file
2 * @brief HoG Service sample
3 */
4
5 /*
6 * Copyright (c) 2016 Intel Corporation
7 *
8 * SPDX-License-Identifier: Apache-2.0
9 */
10
11 #include <zephyr/types.h>
12 #include <zephyr/drivers/gpio.h>
13 #include <stddef.h>
14 #include <string.h>
15 #include <errno.h>
16 #include <zephyr/sys/printk.h>
17 #include <zephyr/sys/byteorder.h>
18 #include <zephyr/kernel.h>
19
20 #include <zephyr/bluetooth/bluetooth.h>
21 #include <zephyr/bluetooth/hci.h>
22 #include <zephyr/bluetooth/conn.h>
23 #include <zephyr/bluetooth/uuid.h>
24 #include <zephyr/bluetooth/gatt.h>
25
26 enum {
27 HIDS_REMOTE_WAKE = BIT(0),
28 HIDS_NORMALLY_CONNECTABLE = BIT(1),
29 };
30
31 struct hids_info {
32 uint16_t version; /* version number of base USB HID Specification */
33 uint8_t code; /* country HID Device hardware is localized for. */
34 uint8_t flags;
35 } __packed;
36
37 struct hids_report {
38 uint8_t id; /* report id */
39 uint8_t type; /* report type */
40 } __packed;
41
42 static struct hids_info info = {
43 .version = 0x0000,
44 .code = 0x00,
45 .flags = HIDS_NORMALLY_CONNECTABLE,
46 };
47
48 enum {
49 HIDS_INPUT = 0x01,
50 HIDS_OUTPUT = 0x02,
51 HIDS_FEATURE = 0x03,
52 };
53
54 static struct hids_report input = {
55 .id = 0x01,
56 .type = HIDS_INPUT,
57 };
58
59 static uint8_t simulate_input;
60 static uint8_t ctrl_point;
61 static uint8_t report_map[] = {
62 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
63 0x09, 0x02, /* Usage (Mouse) */
64 0xA1, 0x01, /* Collection (Application) */
65 0x85, 0x01, /* Report Id (1) */
66 0x09, 0x01, /* Usage (Pointer) */
67 0xA1, 0x00, /* Collection (Physical) */
68 0x05, 0x09, /* Usage Page (Button) */
69 0x19, 0x01, /* Usage Minimum (0x01) */
70 0x29, 0x03, /* Usage Maximum (0x03) */
71 0x15, 0x00, /* Logical Minimum (0) */
72 0x25, 0x01, /* Logical Maximum (1) */
73 0x95, 0x03, /* Report Count (3) */
74 0x75, 0x01, /* Report Size (1) */
75 0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,...) */
76 0x95, 0x01, /* Report Count (1) */
77 0x75, 0x05, /* Report Size (5) */
78 0x81, 0x03, /* Input (Const,Var,Abs,No Wrap,Linear,...) */
79 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
80 0x09, 0x30, /* Usage (X) */
81 0x09, 0x31, /* Usage (Y) */
82 0x15, 0x81, /* Logical Minimum (129) */
83 0x25, 0x7F, /* Logical Maximum (127) */
84 0x75, 0x08, /* Report Size (8) */
85 0x95, 0x02, /* Report Count (2) */
86 0x81, 0x06, /* Input (Data,Var,Rel,No Wrap,Linear,...) */
87 0xC0, /* End Collection */
88 0xC0, /* End Collection */
89 };
90
91
read_info(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)92 static ssize_t read_info(struct bt_conn *conn,
93 const struct bt_gatt_attr *attr, void *buf,
94 uint16_t len, uint16_t offset)
95 {
96 return bt_gatt_attr_read(conn, attr, buf, len, offset, attr->user_data,
97 sizeof(struct hids_info));
98 }
99
read_report_map(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)100 static ssize_t read_report_map(struct bt_conn *conn,
101 const struct bt_gatt_attr *attr, void *buf,
102 uint16_t len, uint16_t offset)
103 {
104 return bt_gatt_attr_read(conn, attr, buf, len, offset, report_map,
105 sizeof(report_map));
106 }
107
read_report(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)108 static ssize_t read_report(struct bt_conn *conn,
109 const struct bt_gatt_attr *attr, void *buf,
110 uint16_t len, uint16_t offset)
111 {
112 return bt_gatt_attr_read(conn, attr, buf, len, offset, attr->user_data,
113 sizeof(struct hids_report));
114 }
115
input_ccc_changed(const struct bt_gatt_attr * attr,uint16_t value)116 static void input_ccc_changed(const struct bt_gatt_attr *attr, uint16_t value)
117 {
118 simulate_input = (value == BT_GATT_CCC_NOTIFY) ? 1 : 0;
119 }
120
read_input_report(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)121 static ssize_t read_input_report(struct bt_conn *conn,
122 const struct bt_gatt_attr *attr, void *buf,
123 uint16_t len, uint16_t offset)
124 {
125 return bt_gatt_attr_read(conn, attr, buf, len, offset, NULL, 0);
126 }
127
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)128 static ssize_t write_ctrl_point(struct bt_conn *conn,
129 const struct bt_gatt_attr *attr,
130 const void *buf, uint16_t len, uint16_t offset,
131 uint8_t flags)
132 {
133 uint8_t *value = attr->user_data;
134
135 if (offset + len > sizeof(ctrl_point)) {
136 return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
137 }
138
139 memcpy(value + offset, buf, len);
140
141 return len;
142 }
143
144 #if CONFIG_SAMPLE_BT_USE_AUTHENTICATION
145 /* Require encryption using authenticated link-key. */
146 #define SAMPLE_BT_PERM_READ BT_GATT_PERM_READ_AUTHEN
147 #define SAMPLE_BT_PERM_WRITE BT_GATT_PERM_WRITE_AUTHEN
148 #else
149 /* Require encryption. */
150 #define SAMPLE_BT_PERM_READ BT_GATT_PERM_READ_ENCRYPT
151 #define SAMPLE_BT_PERM_WRITE BT_GATT_PERM_WRITE_ENCRYPT
152 #endif
153
154 /* HID Service Declaration */
155 BT_GATT_SERVICE_DEFINE(hog_svc,
156 BT_GATT_PRIMARY_SERVICE(BT_UUID_HIDS),
157 BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_INFO, BT_GATT_CHRC_READ,
158 BT_GATT_PERM_READ, read_info, NULL, &info),
159 BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT_MAP, BT_GATT_CHRC_READ,
160 BT_GATT_PERM_READ, read_report_map, NULL, NULL),
161 BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT,
162 BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
163 SAMPLE_BT_PERM_READ,
164 read_input_report, NULL, NULL),
165 BT_GATT_CCC(input_ccc_changed,
166 SAMPLE_BT_PERM_READ | SAMPLE_BT_PERM_WRITE),
167 BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ,
168 read_report, NULL, &input),
169 BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_CTRL_POINT,
170 BT_GATT_CHRC_WRITE_WITHOUT_RESP,
171 BT_GATT_PERM_WRITE,
172 NULL, write_ctrl_point, &ctrl_point),
173 );
174
hog_init(void)175 void hog_init(void)
176 {
177 }
178
179 #define SW0_NODE DT_ALIAS(sw0)
180
hog_button_loop(void)181 void hog_button_loop(void)
182 {
183 #if DT_NODE_HAS_STATUS(SW0_NODE, okay)
184 const struct gpio_dt_spec sw0 = GPIO_DT_SPEC_GET(SW0_NODE, gpios);
185
186 gpio_pin_configure_dt(&sw0, GPIO_INPUT);
187
188 for (;;) {
189 if (simulate_input) {
190 /* HID Report:
191 * Byte 0: buttons (lower 3 bits)
192 * Byte 1: X axis (int8)
193 * Byte 2: Y axis (int8)
194 */
195 int8_t report[3] = {0, 0, 0};
196
197 if (gpio_pin_get_dt(&sw0)) {
198 report[0] |= BIT(0);
199 }
200
201 bt_gatt_notify(NULL, &hog_svc.attrs[5],
202 report, sizeof(report));
203 }
204 k_sleep(K_MSEC(100));
205 }
206 #endif
207 }
208