1 /** @file
2 * @brief Bluetooth Object Transfer Client Sample
3 *
4 * Copyright (c) 2022 Nordic Semiconductor ASA
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #include <errno.h>
10 #include <stddef.h>
11
12 #include <zephyr/bluetooth/bluetooth.h>
13 #include <zephyr/bluetooth/conn.h>
14 #include <zephyr/bluetooth/gatt.h>
15 #include <zephyr/bluetooth/hci.h>
16 #include <zephyr/bluetooth/services/ots.h>
17 #include <zephyr/bluetooth/uuid.h>
18 #include <zephyr/device.h>
19 #include <zephyr/drivers/gpio.h>
20 #include <zephyr/sys/byteorder.h>
21 #include <zephyr/sys/printk.h>
22 #include <zephyr/types.h>
23 #include <zephyr/kernel.h>
24
25 #define OBJ_MAX_SIZE 1024
26 /* Hardcoded here since definition is in internal header */
27 #define BT_GATT_OTS_OLCP_RES_OPERATION_FAILED 0x04
28 #define BT_GATT_OTS_OLCP_RES_OUT_OF_BONDS 0x05
29
30 static struct bt_ots_client otc;
31 static struct bt_ots_client_cb otc_cb;
32 static struct bt_gatt_discover_params discover_params;
33 static struct bt_gatt_subscribe_params *oacp_sub_params;
34 static struct bt_gatt_subscribe_params *olcp_sub_params;
35 static unsigned char obj_data_buf[OBJ_MAX_SIZE];
36 static uint32_t last_checksum;
37
38 static bool first_selected;
39 static void on_obj_selected(struct bt_ots_client *ots_inst, struct bt_conn *conn, int err);
40
41 static void on_obj_metadata_read(struct bt_ots_client *ots_inst, struct bt_conn *conn, int err,
42 uint8_t metadata_read);
43
44 static int on_obj_data_read(struct bt_ots_client *ots_inst, struct bt_conn *conn, uint32_t offset,
45 uint32_t len, uint8_t *data_p, bool is_complete);
46
47 static void start_scan(void);
48 static struct bt_uuid_16 discover_uuid = BT_UUID_INIT_16(0);
49 static struct bt_conn *default_conn;
50 static atomic_t discovery_state;
51
52 enum OTS_SERVICE_DISCOVERY_STATE_BIT {
53 DISC_OTS_FEATURE,
54 DISC_OTS_NAME,
55 DISC_OTS_TYPE,
56 DISC_OTS_SIZE,
57 DISC_OTS_ID,
58 DISC_OTS_PROPERTIES,
59 DISC_OTS_ACTION_CP,
60 DISC_OTS_LIST_CP,
61 };
62
print_hex_number(const uint8_t * num,size_t len)63 static void print_hex_number(const uint8_t *num, size_t len)
64 {
65 printk("0x");
66 for (size_t i = 0; i < len; i++) {
67 printk("%02x ", num[i]);
68 }
69
70 printk("\n");
71 }
72
73 /*
74 * Get buttons configuration from the devicetree sw0~sw3 alias. This is mandatory.
75 */
76 #define SW0_NODE DT_ALIAS(sw0)
77 #define SW1_NODE DT_ALIAS(sw1)
78 #define SW2_NODE DT_ALIAS(sw2)
79 #define SW3_NODE DT_ALIAS(sw3)
80 #if !DT_NODE_HAS_STATUS(SW0_NODE, okay) || !DT_NODE_HAS_STATUS(SW1_NODE, okay) || \
81 !DT_NODE_HAS_STATUS(SW2_NODE, okay) || !DT_NODE_HAS_STATUS(SW3_NODE, okay)
82 #error "Unsupported board: This sample need 4 buttons to run"
83 #endif
84
85 static const struct gpio_dt_spec button0 = GPIO_DT_SPEC_GET_OR(SW0_NODE, gpios, {0});
86 static const struct gpio_dt_spec button1 = GPIO_DT_SPEC_GET_OR(SW1_NODE, gpios, {0});
87 static const struct gpio_dt_spec button2 = GPIO_DT_SPEC_GET_OR(SW2_NODE, gpios, {0});
88 static const struct gpio_dt_spec button3 = GPIO_DT_SPEC_GET_OR(SW3_NODE, gpios, {0});
89 #define BTN_COUNT 4
90
91 static const struct gpio_dt_spec btns[BTN_COUNT] = {button0, button1, button2, button3};
92 static struct gpio_callback button_cb_data;
93 struct otc_btn_work_info {
94 struct k_work_delayable work;
95 uint32_t pins;
96 } otc_btn_work;
97
98 struct otc_checksum_work_info {
99 struct k_work_delayable work;
100 off_t offset;
101 size_t len;
102 } otc_checksum_work;
103
otc_btn_work_fn(struct k_work * work)104 static void otc_btn_work_fn(struct k_work *work)
105 {
106 struct k_work_delayable *dwork = k_work_delayable_from_work(work);
107 struct otc_btn_work_info *btn_work = CONTAINER_OF(dwork, struct otc_btn_work_info, work);
108 int err;
109 size_t size_to_write;
110
111 if (btn_work->pins == BIT(button0.pin)) {
112 if (!first_selected) {
113 err = bt_ots_client_select_id(&otc, default_conn, BT_OTS_OBJ_ID_MIN);
114 first_selected = true;
115 } else {
116 printk("select next\n");
117 err = bt_ots_client_select_next(&otc, default_conn);
118 }
119
120 if (err != 0) {
121 printk("Failed to select object (err %d)\n", err);
122 }
123
124 printk("Selecting object succeeded\n");
125 } else if (btn_work->pins == BIT(button1.pin)) {
126 printk("read OTS object meta\n");
127 err = bt_ots_client_read_object_metadata(&otc, default_conn,
128 BT_OTS_METADATA_REQ_ALL);
129 if (err != 0) {
130 printk("Failed to read object metadata (err %d)\n", err);
131 }
132
133 } else if (btn_work->pins == BIT(button2.pin)) {
134 if (BT_OTS_OBJ_GET_PROP_WRITE(otc.cur_object.props)) {
135 size_to_write = MIN(OBJ_MAX_SIZE, otc.cur_object.size.alloc);
136 (void)memset(obj_data_buf, 0, size_to_write);
137 printk("Going to write OTS object len %d\n", size_to_write);
138 for (uint32_t idx = 0; idx < size_to_write; idx++) {
139 obj_data_buf[idx] = UINT8_MAX - (idx % UINT8_MAX);
140 }
141
142 last_checksum = bt_ots_client_calc_checksum(obj_data_buf, size_to_write);
143 printk("Data sent checksum 0x%08x\n", last_checksum);
144 err = bt_ots_client_write_object_data(&otc, default_conn, obj_data_buf,
145 size_to_write, 0,
146 BT_OTS_OACP_WRITE_OP_MODE_NONE);
147 if (err != 0) {
148 printk("Failed to write object (err %d)\n", err);
149 }
150 } else {
151 printk("This OBJ does not support WRITE OP\n");
152 }
153
154 } else if (btn_work->pins == BIT(button3.pin)) {
155 if (BT_OTS_OBJ_GET_PROP_READ(otc.cur_object.props)) {
156 printk("read OTS object\n");
157 err = bt_ots_client_read_object_data(&otc, default_conn);
158 if (err != 0) {
159 printk("Failed to read object %d\n", err);
160 }
161 } else {
162 printk("This OBJ does not support READ OP\n");
163 }
164 }
165 }
166
otc_checksum_work_fn(struct k_work * work)167 static void otc_checksum_work_fn(struct k_work *work)
168 {
169 struct k_work_delayable *dwork = k_work_delayable_from_work(work);
170 struct otc_checksum_work_info *checksum_work =
171 CONTAINER_OF(dwork, struct otc_checksum_work_info, work);
172 int err;
173
174 err = bt_ots_client_get_object_checksum(&otc, default_conn, checksum_work->offset,
175 checksum_work->len);
176 if (err != 0) {
177 printk("bt_ots_client_get_object_checksum failed (%d)\n", err);
178 }
179 }
180
button_pressed(const struct device * dev,struct gpio_callback * cb,uint32_t pins)181 static void button_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
182 {
183 otc_btn_work.pins = pins;
184 k_work_schedule(&otc_btn_work.work, K_MSEC(100));
185 }
186
configure_button_irq(const struct gpio_dt_spec btn)187 static void configure_button_irq(const struct gpio_dt_spec btn)
188 {
189 int ret;
190
191 if (!gpio_is_ready_dt(&btn)) {
192 printk("Error: button device %s is not ready\n", btn.port->name);
193 return;
194 }
195
196 ret = gpio_pin_configure_dt(&btn, GPIO_INPUT);
197 if (ret != 0) {
198 printk("Error %d: failed to configure %s pin %d\n", ret, btn.port->name, btn.pin);
199 return;
200 }
201
202 ret = gpio_pin_interrupt_configure_dt(&btn, GPIO_INT_EDGE_TO_ACTIVE);
203
204 if (ret != 0) {
205 printk("Error %d: failed to configure interrupt on %s pin %d\n", ret,
206 btn.port->name, btn.pin);
207 return;
208 }
209
210 button_cb_data.pin_mask |= BIT(btn.pin);
211 gpio_add_callback(btn.port, &button_cb_data);
212
213 printk("Set up button at %s pin %d\n", btn.port->name, btn.pin);
214 }
215
configure_buttons(void)216 static void configure_buttons(void)
217 {
218 gpio_init_callback(&button_cb_data, button_pressed, 0);
219
220 for (int idx = 0; idx < BTN_COUNT; idx++) {
221 configure_button_irq(btns[idx]);
222 }
223 }
224
eir_found(struct bt_data * data,void * user_data)225 static bool eir_found(struct bt_data *data, void *user_data)
226 {
227 bt_addr_le_t *addr = user_data;
228 int i;
229
230 switch (data->type) {
231 case BT_DATA_UUID16_SOME:
232 case BT_DATA_UUID16_ALL:
233 if (data->data_len % sizeof(uint16_t) != 0U) {
234 printk("AD malformed\n");
235 return true;
236 }
237
238 for (i = 0; i < data->data_len; i += sizeof(uint16_t)) {
239 struct bt_le_conn_param *param;
240 const struct bt_uuid *uuid;
241 uint16_t u16;
242 int err;
243
244 (void)memcpy(&u16, &data->data[i], sizeof(u16));
245 uuid = BT_UUID_DECLARE_16(sys_le16_to_cpu(u16));
246 if (bt_uuid_cmp(uuid, BT_UUID_OTS) != 0) {
247 continue;
248 }
249
250 err = bt_le_scan_stop();
251 if (err != 0) {
252 printk("Stop LE scan failed (err %d)\n", err);
253 continue;
254 }
255
256 param = BT_LE_CONN_PARAM_DEFAULT;
257 err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, param, &default_conn);
258 if (err != 0) {
259 printk("Create conn failed (err %d)\n", err);
260 start_scan();
261 }
262
263 return false;
264 }
265 }
266 return true;
267 }
268
device_found(const bt_addr_le_t * addr,int8_t rssi,uint8_t type,struct net_buf_simple * ad)269 static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
270 struct net_buf_simple *ad)
271 {
272 char dev[BT_ADDR_LE_STR_LEN];
273
274 bt_addr_le_to_str(addr, dev, sizeof(dev));
275
276 /* We're only interested in connectable events and scan response
277 * because service UUID is in sd of sample peripheral_ots.
278 */
279 if (type == BT_GAP_ADV_TYPE_ADV_IND || type == BT_GAP_ADV_TYPE_ADV_DIRECT_IND ||
280 type == BT_GAP_ADV_TYPE_SCAN_RSP) {
281 bt_data_parse(ad, eir_found, (void *)addr);
282 }
283 }
284
start_scan(void)285 static void start_scan(void)
286 {
287 int err;
288
289 /* Use active scanning and disable duplicate filtering to handle any
290 * devices that might update their advertising data at runtime.
291 */
292 struct bt_le_scan_param scan_param = {
293 .type = BT_LE_SCAN_TYPE_ACTIVE,
294 .options = BT_LE_SCAN_OPT_NONE,
295 .interval = BT_GAP_SCAN_FAST_INTERVAL,
296 .window = BT_GAP_SCAN_FAST_WINDOW,
297 };
298
299 err = bt_le_scan_start(&scan_param, device_found);
300 if (err != 0) {
301 printk("Scanning OTS TAG failed to start (err %d)\n", err);
302 return;
303 }
304
305 printk("Scanning successfully started\n");
306 }
307
subscribe_func(void)308 static int subscribe_func(void)
309 {
310 int ret;
311
312 printk("Subscribe OACP and OLCP Indication\n");
313 oacp_sub_params = &otc.oacp_sub_params;
314 oacp_sub_params->disc_params = &otc.oacp_sub_disc_params;
315 if (oacp_sub_params) {
316 /* With ccc_handle == 0 it will use auto discovery */
317 oacp_sub_params->ccc_handle = 0;
318 oacp_sub_params->end_handle = otc.end_handle;
319 oacp_sub_params->value = BT_GATT_CCC_INDICATE;
320 oacp_sub_params->value_handle = otc.oacp_handle;
321 oacp_sub_params->notify = bt_ots_client_indicate_handler;
322 ret = bt_gatt_subscribe(default_conn, oacp_sub_params);
323
324 if (ret != 0) {
325 printk("Subscribe OACP failed %d\n", ret);
326 return ret;
327 }
328 }
329
330 olcp_sub_params = &otc.olcp_sub_params;
331 olcp_sub_params->disc_params = &otc.olcp_sub_disc_params;
332 if (olcp_sub_params) {
333 /* With ccc_handle == 0 it will use auto discovery */
334 olcp_sub_params->ccc_handle = 0;
335 olcp_sub_params->end_handle = otc.end_handle;
336 olcp_sub_params->value = BT_GATT_CCC_INDICATE;
337 olcp_sub_params->value_handle = otc.olcp_handle;
338 olcp_sub_params->notify = bt_ots_client_indicate_handler;
339 ret = bt_gatt_subscribe(default_conn, olcp_sub_params);
340
341 if (ret != 0) {
342 printk("Subscribe OLCP failed %d\n", ret);
343 return ret;
344 }
345 }
346
347 return ret;
348 }
349
is_discovery_complete(void)350 static bool is_discovery_complete(void)
351 {
352 return (atomic_test_bit(&discovery_state, DISC_OTS_FEATURE) &&
353 atomic_test_bit(&discovery_state, DISC_OTS_NAME) &&
354 atomic_test_bit(&discovery_state, DISC_OTS_TYPE) &&
355 atomic_test_bit(&discovery_state, DISC_OTS_SIZE) &&
356 atomic_test_bit(&discovery_state, DISC_OTS_ID) &&
357 atomic_test_bit(&discovery_state, DISC_OTS_PROPERTIES) &&
358 atomic_test_bit(&discovery_state, DISC_OTS_ACTION_CP) &&
359 atomic_test_bit(&discovery_state, DISC_OTS_LIST_CP));
360 }
361
discover_func(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)362 static uint8_t discover_func(struct bt_conn *conn, const struct bt_gatt_attr *attr,
363 struct bt_gatt_discover_params *params)
364 {
365 int err;
366
367 if (!attr) {
368 printk("Discover complete\n");
369 (void)memset(params, 0, sizeof(*params));
370 return BT_GATT_ITER_STOP;
371 }
372
373 if (bt_uuid_cmp(discover_params.uuid, BT_UUID_OTS) == 0) {
374 (void)memcpy(&discover_uuid, BT_UUID_OTS_FEATURE, sizeof(discover_uuid));
375 discover_params.uuid = &discover_uuid.uuid;
376 discover_params.start_handle = attr->handle + 1;
377 discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
378 err = bt_gatt_discover(conn, &discover_params);
379
380 if (err != 0) {
381 printk("Discover failed (err %d)\n", err);
382 }
383
384 } else if (bt_uuid_cmp(discover_params.uuid, BT_UUID_OTS_FEATURE) == 0) {
385 atomic_set_bit(&discovery_state, DISC_OTS_FEATURE);
386 otc.feature_handle = bt_gatt_attr_value_handle(attr);
387 (void)memcpy(&discover_uuid, BT_UUID_OTS_NAME, sizeof(discover_uuid));
388 discover_params.uuid = &discover_uuid.uuid;
389 discover_params.start_handle = attr->handle + 1;
390 discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
391
392 err = bt_gatt_discover(conn, &discover_params);
393 if (err != 0) {
394 printk("Discover failed (err %d)\n", err);
395 }
396
397 } else if (bt_uuid_cmp(discover_params.uuid, BT_UUID_OTS_NAME) == 0) {
398 atomic_set_bit(&discovery_state, DISC_OTS_NAME);
399 otc.obj_name_handle = bt_gatt_attr_value_handle(attr);
400 (void)memcpy(&discover_uuid, BT_UUID_OTS_TYPE, sizeof(discover_uuid));
401 discover_params.uuid = &discover_uuid.uuid;
402 discover_params.start_handle = attr->handle + 1;
403 discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
404
405 err = bt_gatt_discover(conn, &discover_params);
406 if (err != 0) {
407 printk("Discover failed (err %d)\n", err);
408 }
409
410 } else if (bt_uuid_cmp(discover_params.uuid, BT_UUID_OTS_TYPE) == 0) {
411 atomic_set_bit(&discovery_state, DISC_OTS_TYPE);
412 otc.obj_type_handle = bt_gatt_attr_value_handle(attr);
413 (void)memcpy(&discover_uuid, BT_UUID_OTS_SIZE, sizeof(discover_uuid));
414 discover_params.uuid = &discover_uuid.uuid;
415 discover_params.start_handle = attr->handle + 1;
416 discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
417
418 err = bt_gatt_discover(conn, &discover_params);
419 if (err != 0) {
420 printk("Discover failed (err %d)\n", err);
421 }
422
423 } else if (bt_uuid_cmp(discover_params.uuid, BT_UUID_OTS_SIZE) == 0) {
424 atomic_set_bit(&discovery_state, DISC_OTS_SIZE);
425 otc.obj_size_handle = bt_gatt_attr_value_handle(attr);
426 (void)memcpy(&discover_uuid, BT_UUID_OTS_ID, sizeof(discover_uuid));
427 discover_params.uuid = &discover_uuid.uuid;
428 discover_params.start_handle = attr->handle + 1;
429 discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
430
431 err = bt_gatt_discover(conn, &discover_params);
432 if (err != 0) {
433 printk("Discover failed (err %d)\n", err);
434 }
435
436 } else if (bt_uuid_cmp(discover_params.uuid, BT_UUID_OTS_ID) == 0) {
437 atomic_set_bit(&discovery_state, DISC_OTS_ID);
438 otc.obj_id_handle = bt_gatt_attr_value_handle(attr);
439 (void)memcpy(&discover_uuid, BT_UUID_OTS_PROPERTIES, sizeof(discover_uuid));
440 discover_params.uuid = &discover_uuid.uuid;
441 discover_params.start_handle = attr->handle + 1;
442 discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
443
444 err = bt_gatt_discover(conn, &discover_params);
445 if (err != 0) {
446 printk("Discover failed (err %d)\n", err);
447 }
448
449 } else if (bt_uuid_cmp(discover_params.uuid, BT_UUID_OTS_PROPERTIES) == 0) {
450 atomic_set_bit(&discovery_state, DISC_OTS_PROPERTIES);
451 otc.obj_properties_handle = bt_gatt_attr_value_handle(attr);
452 (void)memcpy(&discover_uuid, BT_UUID_OTS_ACTION_CP, sizeof(discover_uuid));
453 discover_params.uuid = &discover_uuid.uuid;
454 discover_params.start_handle = attr->handle + 1;
455 discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
456
457 err = bt_gatt_discover(conn, &discover_params);
458 if (err != 0) {
459 printk("Discover failed (err %d)\n", err);
460 }
461 } else if (bt_uuid_cmp(discover_params.uuid, BT_UUID_OTS_ACTION_CP) == 0) {
462 atomic_set_bit(&discovery_state, DISC_OTS_ACTION_CP);
463 otc.oacp_handle = bt_gatt_attr_value_handle(attr);
464 (void)memcpy(&discover_uuid, BT_UUID_OTS_LIST_CP, sizeof(discover_uuid));
465 discover_params.uuid = &discover_uuid.uuid;
466 discover_params.start_handle = attr->handle + 1;
467 discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
468
469 err = bt_gatt_discover(conn, &discover_params);
470 if (err != 0) {
471 printk("Discover failed (err %d)\n", err);
472 }
473 } else if (bt_uuid_cmp(discover_params.uuid, BT_UUID_OTS_LIST_CP) == 0) {
474 atomic_set_bit(&discovery_state, DISC_OTS_LIST_CP);
475 otc.olcp_handle = bt_gatt_attr_value_handle(attr);
476 } else {
477 return BT_GATT_ITER_STOP;
478 }
479
480 if (is_discovery_complete()) {
481 printk("Discovery complete for OTS Client\n");
482 err = subscribe_func();
483
484 if (err != 0) {
485 return BT_GATT_ITER_STOP;
486 }
487
488 /* Read feature of OTS server*/
489 err = bt_ots_client_read_feature(&otc, default_conn);
490 if (err != 0) {
491 printk("bt_ots_client_read_feature failed (err %d)", err);
492 }
493 }
494
495 return BT_GATT_ITER_STOP;
496 }
497
connected(struct bt_conn * conn,uint8_t err)498 static void connected(struct bt_conn *conn, uint8_t err)
499 {
500 char addr[BT_ADDR_LE_STR_LEN];
501
502 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
503 first_selected = false;
504 if (err != 0) {
505 printk("Failed to connect to %s (%u)\n", addr, err);
506
507 bt_conn_unref(default_conn);
508 default_conn = NULL;
509 start_scan();
510 return;
511 }
512
513 if (conn != default_conn) {
514 return;
515 }
516
517 printk("Connected: %s\n", addr);
518
519 if (conn == default_conn) {
520 (void)memcpy(&discover_uuid, BT_UUID_OTS, sizeof(discover_uuid));
521 discover_params.uuid = &discover_uuid.uuid;
522 discover_params.func = discover_func;
523 discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
524 discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
525 discover_params.type = BT_GATT_DISCOVER_PRIMARY;
526
527 err = bt_gatt_discover(default_conn, &discover_params);
528 if (err != 0) {
529 printk("Discover failed(err %d)\n", err);
530 return;
531 }
532 }
533 }
534
disconnected(struct bt_conn * conn,uint8_t reason)535 static void disconnected(struct bt_conn *conn, uint8_t reason)
536 {
537 char addr[BT_ADDR_LE_STR_LEN];
538
539 if (conn != default_conn) {
540 return;
541 }
542
543 bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
544
545 printk("Disconnected: %s (reason 0x%02x)\n", addr, reason);
546
547 bt_conn_unref(default_conn);
548 default_conn = NULL;
549 discovery_state = ATOMIC_INIT(0);
550 start_scan();
551 }
552
553 BT_CONN_CB_DEFINE(conn_callbacks) = {
554 .connected = connected,
555 .disconnected = disconnected,
556 };
557
on_obj_selected(struct bt_ots_client * ots_inst,struct bt_conn * conn,int err)558 static void on_obj_selected(struct bt_ots_client *ots_inst, struct bt_conn *conn, int err)
559 {
560 printk("Current object selected cb OLCP result (%d)\n", err);
561
562 if (err == BT_GATT_OTS_OLCP_RES_OPERATION_FAILED) {
563 printk("BT_GATT_OTS_OLCP_RES_OPERATION_FAILED %d\n", err);
564 first_selected = false;
565 } else if (err == BT_GATT_OTS_OLCP_RES_OUT_OF_BONDS) {
566 printk("BT_GATT_OTS_OLCP_RES_OUT_OF_BONDS %d. Select first valid instead\n", err);
567 (void)bt_ots_client_select_id(&otc, default_conn, BT_OTS_OBJ_ID_MIN);
568 }
569
570 (void)memset(obj_data_buf, 0, OBJ_MAX_SIZE);
571 }
572
on_obj_data_read(struct bt_ots_client * ots_inst,struct bt_conn * conn,uint32_t offset,uint32_t len,uint8_t * data_p,bool is_complete)573 static int on_obj_data_read(struct bt_ots_client *ots_inst, struct bt_conn *conn, uint32_t offset,
574 uint32_t len, uint8_t *data_p, bool is_complete)
575 {
576 printk("Received OTS Object content, %i bytes at offset %i\n", len, offset);
577
578 print_hex_number(data_p, len);
579
580 if ((offset + len) > OBJ_MAX_SIZE) {
581 printk("Can not fit whole object, drop the rest of data\n");
582 } else {
583 (void)memcpy((obj_data_buf + offset), data_p, MIN((OBJ_MAX_SIZE - offset), len));
584 }
585
586 if (is_complete) {
587 printk("Object total received %d\n", len + offset);
588 print_hex_number(obj_data_buf, len + offset);
589 (void)memset(obj_data_buf, 0, OBJ_MAX_SIZE);
590 otc_checksum_work.offset = 0;
591 otc_checksum_work.len = otc.cur_object.size.cur;
592 k_work_schedule(&otc_checksum_work.work, K_NO_WAIT);
593 return BT_OTS_STOP;
594 }
595
596 return BT_OTS_CONTINUE;
597 }
598
on_obj_metadata_read(struct bt_ots_client * ots_inst,struct bt_conn * conn,int err,uint8_t metadata_read)599 static void on_obj_metadata_read(struct bt_ots_client *ots_inst, struct bt_conn *conn, int err,
600 uint8_t metadata_read)
601 {
602 printk("Object's meta data:\n");
603 printk("\tCurrent size\t:%u", ots_inst->cur_object.size.cur);
604 printk("\tAlloc size\t:%u\n", ots_inst->cur_object.size.alloc);
605
606 if (ots_inst->cur_object.size.cur > OBJ_MAX_SIZE) {
607 printk("Object larger than allocated buffer\n");
608 }
609
610 bt_ots_metadata_display(&ots_inst->cur_object, 1);
611 }
on_obj_data_written(struct bt_ots_client * ots_inst,struct bt_conn * conn,size_t len)612 static void on_obj_data_written(struct bt_ots_client *ots_inst, struct bt_conn *conn, size_t len)
613 {
614 int err;
615
616 printk("Object been written %d\n", len);
617 /* Update object size after write done*/
618 err = bt_ots_client_read_object_metadata(&otc, default_conn,
619 BT_OTS_METADATA_REQ_ALL);
620 if (err != 0) {
621 printk("Failed to read object metadata (err %d)\n", err);
622 }
623 }
624
on_obj_checksum_calculated(struct bt_ots_client * ots_inst,struct bt_conn * conn,int err,uint32_t checksum)625 void on_obj_checksum_calculated(struct bt_ots_client *ots_inst,
626 struct bt_conn *conn, int err, uint32_t checksum)
627 {
628 printk("Object Calculate checksum OACP result (%d)\nChecksum 0x%08x last sent 0x%08x %s\n",
629 err, checksum, last_checksum, (checksum == last_checksum) ? "match" : "not match");
630 }
631
bt_otc_init(void)632 static void bt_otc_init(void)
633 {
634 otc_cb.obj_data_read = on_obj_data_read;
635 otc_cb.obj_selected = on_obj_selected;
636 otc_cb.obj_metadata_read = on_obj_metadata_read;
637 otc_cb.obj_data_written = on_obj_data_written;
638 otc_cb.obj_checksum_calculated = on_obj_checksum_calculated;
639 otc.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
640 otc.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
641 printk("Current object selected callback: %p\n", otc_cb.obj_selected);
642 printk("Content callback: %p\n", otc_cb.obj_data_read);
643 printk("Metadata callback: %p\n", otc_cb.obj_metadata_read);
644 otc.cb = &otc_cb;
645 bt_ots_client_register(&otc);
646 }
647
main(void)648 int main(void)
649 {
650 int err;
651
652 first_selected = false;
653 discovery_state = ATOMIC_INIT(0);
654 k_work_init_delayable(&otc_btn_work.work, otc_btn_work_fn);
655 k_work_init_delayable(&otc_checksum_work.work, otc_checksum_work_fn);
656
657 configure_buttons();
658 err = bt_enable(NULL);
659
660 if (err != 0) {
661 printk("Bluetooth init failed (err %d)\n", err);
662 return 0;
663 }
664
665 bt_otc_init();
666 printk("Bluetooth OTS client sample running\n");
667
668 start_scan();
669 return 0;
670 }
671