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