1 /*
2 * Copyright (c) 2017 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/sys/printk.h>
9 #include <zephyr/drivers/gpio.h>
10 #include <zephyr/device.h>
11 #include <string.h>
12
13 #include <zephyr/display/mb_display.h>
14
15 #include <zephyr/bluetooth/bluetooth.h>
16 #include <zephyr/bluetooth/uuid.h>
17 #include <zephyr/bluetooth/conn.h>
18 #include <zephyr/bluetooth/gatt.h>
19 #include <zephyr/bluetooth/hci.h>
20
21
22 #include "pong.h"
23
24 #define SCAN_TIMEOUT K_SECONDS(2)
25
26 #define APPEARANCE 0
27
28 #define PONG_SVC_UUID \
29 BT_UUID_128_ENCODE(0xf94fea38, 0x4e24, 0x7ea1, 0x0d4d, 0x6fee0f556c90)
30 #define PONG_CHR_UUID \
31 BT_UUID_128_ENCODE(0xabbf8f1c, 0xc56a, 0x82b5, 0xc640, 0x2ccdd7af94dd)
32
33 static const struct bt_uuid_128 pong_svc_uuid = BT_UUID_INIT_128(PONG_SVC_UUID);
34 static const struct bt_uuid_128 pong_chr_uuid = BT_UUID_INIT_128(PONG_CHR_UUID);
35 static const struct bt_uuid *gatt_ccc_uuid = BT_UUID_GATT_CCC;
36
37 static struct bt_gatt_discover_params discov_param;
38 static struct bt_gatt_subscribe_params subscribe_param;
39
40 static const struct bt_data ad[] = {
41 BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
42 BT_DATA_BYTES(BT_DATA_UUID128_ALL, PONG_SVC_UUID),
43 };
44
45 static const struct bt_data sd[] = {
46 BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
47 };
48
49 static struct bt_conn *default_conn;
50
51 static const struct bt_gatt_attr *local_attr;
52 static uint16_t remote_handle;
53 static bool remote_ready;
54 static bool initiator;
55
56 static struct k_work_delayable ble_work;
57
58 static bool connect_canceled;
59
60 static enum {
61 BLE_DISCONNECTED,
62 BLE_SCAN_START,
63 BLE_SCAN,
64 BLE_CONNECT_CREATE,
65 BLE_CONNECT_CANCEL,
66 BLE_ADV_START,
67 BLE_ADVERTISING,
68 BLE_CONNECTED,
69 } ble_state;
70
71 enum {
72 BLE_BALL_INFO = 0x00,
73 BLE_LOST = 0x01,
74 };
75
76 struct ble_ball_info {
77 int8_t x_pos;
78 int8_t y_pos;
79 int8_t x_vel;
80 int8_t y_vel;
81 } __packed;
82
83 struct ble_data {
84 uint8_t op;
85 union {
86 struct ble_ball_info ball;
87 };
88 } __packed;
89
90 #define BALL_INFO_LEN (1 + sizeof(struct ble_ball_info))
91
ble_send_ball(int8_t x_pos,int8_t y_pos,int8_t x_vel,int8_t y_vel)92 void ble_send_ball(int8_t x_pos, int8_t y_pos, int8_t x_vel, int8_t y_vel)
93 {
94 struct ble_data data = {
95 .op = BLE_BALL_INFO,
96 .ball.x_pos = x_pos,
97 .ball.y_pos = y_pos,
98 .ball.x_vel = x_vel,
99 .ball.y_vel = y_vel,
100 };
101 int err;
102
103 if (!default_conn || !remote_ready) {
104 printk("ble_send_ball(): not ready\n");
105 return;
106 }
107
108 printk("ble_send_ball(%d, %d, %d, %d)\n", x_pos, y_pos, x_vel, y_vel);
109
110 err = bt_gatt_notify(default_conn, local_attr, &data, BALL_INFO_LEN);
111 if (err) {
112 printk("GATT notify failed (err %d)\n", err);
113 }
114 }
115
ble_send_lost(void)116 void ble_send_lost(void)
117 {
118 uint8_t lost = BLE_LOST;
119 int err;
120
121 if (!default_conn || !remote_ready) {
122 printk("ble_send_lost(): not ready\n");
123 return;
124 }
125
126 err = bt_gatt_notify(default_conn, local_attr, &lost, sizeof(lost));
127 if (err) {
128 printk("GATT notify failed (err %d)\n", err);
129 }
130 }
131
notify_func(struct bt_conn * conn,struct bt_gatt_subscribe_params * param,const void * buf,uint16_t len)132 static uint8_t notify_func(struct bt_conn *conn,
133 struct bt_gatt_subscribe_params *param,
134 const void *buf, uint16_t len)
135 {
136 const struct ble_data *data = buf;
137
138 printk("notify_func() data %p len %u\n", data, len);
139
140 if (!data || !len) {
141 printk("Unsubscribed, disconnecting...\n");
142 remote_handle = 0U;
143 if (default_conn) {
144 bt_conn_disconnect(default_conn,
145 BT_HCI_ERR_REMOTE_USER_TERM_CONN);
146 }
147 return BT_GATT_ITER_STOP;
148 }
149
150 switch (data->op) {
151 case BLE_BALL_INFO:
152 if (len < BALL_INFO_LEN) {
153 printk("Too small ball info\n");
154 break;
155 }
156
157 pong_ball_received(data->ball.x_pos, data->ball.y_pos,
158 data->ball.x_vel, data->ball.y_vel);
159 break;
160 case BLE_LOST:
161 pong_remote_lost();
162 break;
163 default:
164 printk("Unknown op 0x%02x\n", data->op);
165 }
166
167 return BT_GATT_ITER_CONTINUE;
168 }
169
discover_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * param)170 static uint8_t discover_func(struct bt_conn *conn,
171 const struct bt_gatt_attr *attr,
172 struct bt_gatt_discover_params *param)
173 {
174 int err;
175
176 if (!attr) {
177 printk("Discover complete\n");
178 (void)memset(&discov_param, 0, sizeof(discov_param));
179 return BT_GATT_ITER_STOP;
180 }
181
182 printk("Attribute handle %u\n", attr->handle);
183
184 if (param->uuid == &pong_svc_uuid.uuid) {
185 printk("Pong service discovered\n");
186 discov_param.uuid = &pong_chr_uuid.uuid;
187 discov_param.start_handle = attr->handle + 1;
188 discov_param.type = BT_GATT_DISCOVER_CHARACTERISTIC;
189
190 err = bt_gatt_discover(conn, &discov_param);
191 if (err) {
192 printk("Char Discovery failed (err %d)\n", err);
193 }
194 } else if (param->uuid == &pong_chr_uuid.uuid) {
195 printk("Pong characteristic discovered\n");
196 discov_param.uuid = gatt_ccc_uuid;
197 discov_param.start_handle = attr->handle + 2;
198 discov_param.type = BT_GATT_DISCOVER_DESCRIPTOR;
199 subscribe_param.value_handle = attr->handle + 1;
200
201 err = bt_gatt_discover(conn, &discov_param);
202 if (err) {
203 printk("CCC Discovery failed (err %d)\n", err);
204 }
205 } else {
206 printk("Pong CCC discovered\n");
207
208 subscribe_param.notify = notify_func;
209 subscribe_param.value = BT_GATT_CCC_NOTIFY;
210 subscribe_param.ccc_handle = attr->handle;
211
212 printk("CCC handle 0x%04x Value handle 0x%04x\n",
213 subscribe_param.ccc_handle,
214 subscribe_param.value_handle);
215
216 err = bt_gatt_subscribe(conn, &subscribe_param);
217 if (err && err != -EALREADY) {
218 printk("Subscribe failed (err %d)\n", err);
219 } else {
220 printk("Subscribed\n");
221 }
222
223 remote_handle = attr->handle;
224 }
225
226 if (remote_handle && remote_ready) {
227 pong_conn_ready(initiator);
228 }
229
230 return BT_GATT_ITER_STOP;
231 }
232
connected(struct bt_conn * conn,uint8_t err)233 static void connected(struct bt_conn *conn, uint8_t err)
234 {
235 struct bt_conn_info info;
236
237 if (err) {
238 printk("Connection failed, err 0x%02x %s\n", err, bt_hci_err_to_str(err));
239 return;
240 }
241
242 if (ble_state == BLE_ADVERTISING) {
243 bt_le_adv_stop();
244 }
245
246 if (!default_conn) {
247 default_conn = bt_conn_ref(conn);
248 }
249
250 bt_conn_get_info(conn, &info);
251 initiator = (info.role == BT_CONN_ROLE_CENTRAL);
252 remote_ready = false;
253 remote_handle = 0U;
254
255 printk("Connected\n");
256 ble_state = BLE_CONNECTED;
257
258 k_work_reschedule(&ble_work, K_NO_WAIT);
259 }
260
disconnected(struct bt_conn * conn,uint8_t reason)261 static void disconnected(struct bt_conn *conn, uint8_t reason)
262 {
263 printk("Disconnected, reason 0x%02x %s\n", reason, bt_hci_err_to_str(reason));
264
265 if (default_conn) {
266 bt_conn_unref(default_conn);
267 default_conn = NULL;
268 }
269
270 remote_handle = 0U;
271
272 if (ble_state == BLE_CONNECTED) {
273 ble_state = BLE_DISCONNECTED;
274 pong_remote_disconnected();
275 }
276 }
277
278 BT_CONN_CB_DEFINE(conn_callbacks) = {
279 .connected = connected,
280 .disconnected = disconnected,
281 };
282
ble_connect(void)283 void ble_connect(void)
284 {
285 if (ble_state != BLE_DISCONNECTED) {
286 printk("Not ready to connect\n");
287 return;
288 }
289
290 ble_state = BLE_SCAN_START;
291 k_work_reschedule(&ble_work, K_NO_WAIT);
292 }
293
ble_cancel_connect(void)294 void ble_cancel_connect(void)
295 {
296 printk("ble_cancel_connect()\n");
297
298 switch (ble_state) {
299 case BLE_SCAN_START:
300 ble_state = BLE_DISCONNECTED;
301 __fallthrough;
302 case BLE_DISCONNECTED:
303 /* If this fails, the handler will run without doing anything,
304 * as the switch case for BLE_DISCONNECTED is empty.
305 */
306 k_work_cancel_delayable(&ble_work);
307 break;
308 case BLE_SCAN:
309 connect_canceled = true;
310 k_work_reschedule(&ble_work, K_NO_WAIT);
311 break;
312 case BLE_ADV_START:
313 ble_state = BLE_DISCONNECTED;
314 break;
315 case BLE_ADVERTISING:
316 connect_canceled = true;
317 k_work_reschedule(&ble_work, K_NO_WAIT);
318 break;
319 case BLE_CONNECT_CREATE:
320 ble_state = BLE_CONNECT_CANCEL;
321 __fallthrough;
322 case BLE_CONNECTED:
323 connect_canceled = true;
324 k_work_reschedule(&ble_work, K_NO_WAIT);
325 break;
326 case BLE_CONNECT_CANCEL:
327 break;
328 }
329 }
330
pong_uuid_match(const uint8_t * data,uint8_t len)331 static bool pong_uuid_match(const uint8_t *data, uint8_t len)
332 {
333 while (len >= 16U) {
334 if (!memcmp(data, pong_svc_uuid.val, 16)) {
335 return true;
336 }
337
338 len -= 16U;
339 data += 16;
340 }
341
342 return false;
343 }
344
create_conn(const bt_addr_le_t * addr)345 static void create_conn(const bt_addr_le_t *addr)
346 {
347 int err;
348
349 if (default_conn) {
350 return;
351 }
352
353 printk("Found matching device, initiating connection...\n");
354
355 err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN,
356 BT_LE_CONN_PARAM_DEFAULT, &default_conn);
357 if (err) {
358 printk("Failed to initiate connection");
359 return;
360 }
361
362 ble_state = BLE_CONNECT_CREATE;
363 k_work_reschedule(&ble_work, SCAN_TIMEOUT);
364 }
365
device_found(const bt_addr_le_t * addr,int8_t rssi,uint8_t type,struct net_buf_simple * ad_buf)366 static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
367 struct net_buf_simple *ad_buf)
368 {
369 if (type != BT_GAP_ADV_TYPE_ADV_IND) {
370 return;
371 }
372
373 while (ad_buf->len > 1) {
374 uint8_t len = net_buf_simple_pull_u8(ad_buf);
375 uint8_t ad_type;
376
377 /* Check for early termination */
378 if (len == 0U) {
379 return;
380 }
381
382 if (len > ad_buf->len) {
383 printk("AD malformed\n");
384 return;
385 }
386
387 ad_type = net_buf_simple_pull_u8(ad_buf);
388 if (ad_type == BT_DATA_UUID128_ALL &&
389 pong_uuid_match(ad_buf->data, len - 1)) {
390 bt_le_scan_stop();
391 create_conn(addr);
392 return;
393 }
394
395 net_buf_simple_pull(ad_buf, len - 1);
396 }
397 }
398
adv_timeout(void)399 static uint32_t adv_timeout(void)
400 {
401 uint32_t timeout;
402
403 if (bt_rand(&timeout, sizeof(timeout)) < 0) {
404 return 10 * MSEC_PER_SEC;
405 }
406
407 timeout %= (10 * MSEC_PER_SEC);
408
409 return timeout + (1 * MSEC_PER_SEC);
410 }
411
cancel_connect(void)412 static void cancel_connect(void)
413 {
414 connect_canceled = false;
415
416 switch (ble_state) {
417 case BLE_SCAN:
418 bt_le_scan_stop();
419 break;
420 case BLE_ADVERTISING:
421 bt_le_adv_stop();
422 break;
423 case BLE_CONNECT_CREATE:
424 case BLE_CONNECTED:
425 bt_conn_disconnect(default_conn,
426 BT_HCI_ERR_REMOTE_USER_TERM_CONN);
427 break;
428 default:
429 break;
430 }
431
432 /* For CONNECTED the state will be updated in the disconnected cb */
433 if (ble_state != BLE_CONNECTED) {
434 ble_state = BLE_DISCONNECTED;
435 }
436 }
437
ble_timeout(struct k_work * work)438 static void ble_timeout(struct k_work *work)
439 {
440 int err;
441
442 if (connect_canceled) {
443 cancel_connect();
444 return;
445 }
446
447 switch (ble_state) {
448 case BLE_DISCONNECTED:
449 break;
450 case BLE_SCAN_START:
451 err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
452 if (err) {
453 printk("Scanning failed to start (err %d)\n", err);
454 }
455
456 printk("Started scanning for devices\n");
457 ble_state = BLE_SCAN;
458 k_work_reschedule(&ble_work, SCAN_TIMEOUT);
459 break;
460 case BLE_CONNECT_CREATE:
461 printk("Connection attempt timed out\n");
462 bt_conn_disconnect(default_conn,
463 BT_HCI_ERR_REMOTE_USER_TERM_CONN);
464 ble_state = BLE_ADV_START;
465 k_work_reschedule(&ble_work, K_NO_WAIT);
466 break;
467 case BLE_SCAN:
468 printk("No devices found during scan\n");
469 bt_le_scan_stop();
470 ble_state = BLE_ADV_START;
471 k_work_reschedule(&ble_work, K_NO_WAIT);
472 break;
473 case BLE_ADV_START:
474 err = bt_le_adv_start(BT_LE_ADV_CONN_ONE_TIME, ad, ARRAY_SIZE(ad), sd,
475 ARRAY_SIZE(sd));
476 if (err) {
477 printk("Advertising failed to start (err %d)\n", err);
478 return;
479 }
480
481 printk("Advertising successfully started\n");
482 ble_state = BLE_ADVERTISING;
483 k_work_reschedule(&ble_work, K_MSEC(adv_timeout()));
484 break;
485 case BLE_ADVERTISING:
486 printk("Timed out advertising\n");
487 bt_le_adv_stop();
488 ble_state = BLE_SCAN_START;
489 k_work_reschedule(&ble_work, K_NO_WAIT);
490 break;
491 case BLE_CONNECTED:
492 discov_param.uuid = &pong_svc_uuid.uuid;
493 discov_param.func = discover_func;
494 discov_param.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
495 discov_param.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
496 discov_param.type = BT_GATT_DISCOVER_PRIMARY;
497
498 err = bt_gatt_discover(default_conn, &discov_param);
499 if (err) {
500 printk("Discover failed (err %d)\n", err);
501 return;
502 }
503 break;
504 case BLE_CONNECT_CANCEL:
505 break;
506 }
507 }
508
pong_ccc_cfg_changed(const struct bt_gatt_attr * attr,uint16_t val)509 static void pong_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t val)
510 {
511 printk("val %u\n", val);
512
513 remote_ready = (val == BT_GATT_CCC_NOTIFY);
514
515 if (remote_ready && remote_handle) {
516 pong_conn_ready(initiator);
517 }
518 }
519
520 BT_GATT_SERVICE_DEFINE(pong_svc,
521 /* Vendor Primary Service Declaration */
522 BT_GATT_PRIMARY_SERVICE(&pong_svc_uuid.uuid),
523 BT_GATT_CHARACTERISTIC(&pong_chr_uuid.uuid, BT_GATT_CHRC_NOTIFY,
524 BT_GATT_PERM_NONE, NULL, NULL, NULL),
525 BT_GATT_CCC(pong_ccc_cfg_changed,
526 BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
527 );
528
ble_init(void)529 void ble_init(void)
530 {
531 int err;
532
533 err = bt_enable(NULL);
534 if (err) {
535 printk("Enabling Bluetooth failed (err %d)\n", err);
536 return;
537 }
538
539 k_work_init_delayable(&ble_work, ble_timeout);
540
541 local_attr = &pong_svc.attrs[1];
542 }
543