| /* |
| * Copyright (c) 2020 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/types.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <zephyr/sys/printk.h> |
| #include <zephyr/kernel.h> |
| |
| #include <zephyr/bluetooth/bluetooth.h> |
| #include <zephyr/bluetooth/conn.h> |
| #include <zephyr/bluetooth/gatt.h> |
| |
| #include <zephyr/bluetooth/services/ots.h> |
| |
| #define DEVICE_NAME CONFIG_BT_DEVICE_NAME |
| #define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1) |
| |
| #define OBJ_POOL_SIZE CONFIG_BT_OTS_MAX_OBJ_CNT |
| #define OBJ_MAX_SIZE 100 |
| |
| static const struct bt_data ad[] = { |
| BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), |
| BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN), |
| }; |
| |
| static const struct bt_data sd[] = { |
| BT_DATA_BYTES(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(BT_UUID_OTS_VAL)), |
| }; |
| |
| static struct { |
| uint8_t data[OBJ_MAX_SIZE]; |
| char name[CONFIG_BT_OTS_OBJ_MAX_NAME_LEN + 1]; |
| } objects[OBJ_POOL_SIZE]; |
| static uint32_t obj_cnt; |
| |
| struct object_creation_data { |
| struct bt_ots_obj_size size; |
| char *name; |
| uint32_t props; |
| }; |
| |
| static struct object_creation_data *object_being_created; |
| |
| static void connected(struct bt_conn *conn, uint8_t err) |
| { |
| if (err) { |
| printk("Connection failed (err %u)\n", err); |
| return; |
| } |
| |
| printk("Connected\n"); |
| } |
| |
| static void disconnected(struct bt_conn *conn, uint8_t reason) |
| { |
| printk("Disconnected (reason %u)\n", reason); |
| } |
| |
| BT_CONN_CB_DEFINE(conn_callbacks) = { |
| .connected = connected, |
| .disconnected = disconnected, |
| }; |
| |
| static int ots_obj_created(struct bt_ots *ots, struct bt_conn *conn, uint64_t id, |
| const struct bt_ots_obj_add_param *add_param, |
| struct bt_ots_obj_created_desc *created_desc) |
| { |
| char id_str[BT_OTS_OBJ_ID_STR_LEN]; |
| uint64_t index; |
| |
| bt_ots_obj_id_to_str(id, id_str, sizeof(id_str)); |
| |
| if (obj_cnt >= ARRAY_SIZE(objects)) { |
| printk("No item from Object pool is available for Object " |
| "with %s ID\n", id_str); |
| return -ENOMEM; |
| } |
| |
| if (add_param->size > OBJ_MAX_SIZE) { |
| printk("Object pool item is too small for Object with %s ID\n", |
| id_str); |
| return -ENOMEM; |
| } |
| |
| if (object_being_created) { |
| created_desc->name = object_being_created->name; |
| created_desc->size = object_being_created->size; |
| created_desc->props = object_being_created->props; |
| } else { |
| index = id - BT_OTS_OBJ_ID_MIN; |
| objects[index].name[0] = '\0'; |
| |
| created_desc->name = objects[index].name; |
| created_desc->size.alloc = OBJ_MAX_SIZE; |
| BT_OTS_OBJ_SET_PROP_READ(created_desc->props); |
| BT_OTS_OBJ_SET_PROP_WRITE(created_desc->props); |
| BT_OTS_OBJ_SET_PROP_PATCH(created_desc->props); |
| BT_OTS_OBJ_SET_PROP_DELETE(created_desc->props); |
| } |
| |
| printk("Object with %s ID has been created\n", id_str); |
| obj_cnt++; |
| |
| return 0; |
| } |
| |
| static int ots_obj_deleted(struct bt_ots *ots, struct bt_conn *conn, |
| uint64_t id) |
| { |
| char id_str[BT_OTS_OBJ_ID_STR_LEN]; |
| |
| bt_ots_obj_id_to_str(id, id_str, sizeof(id_str)); |
| |
| printk("Object with %s ID has been deleted\n", id_str); |
| |
| obj_cnt--; |
| |
| return 0; |
| } |
| |
| static void ots_obj_selected(struct bt_ots *ots, struct bt_conn *conn, |
| uint64_t id) |
| { |
| char id_str[BT_OTS_OBJ_ID_STR_LEN]; |
| |
| bt_ots_obj_id_to_str(id, id_str, sizeof(id_str)); |
| |
| printk("Object with %s ID has been selected\n", id_str); |
| } |
| |
| static ssize_t ots_obj_read(struct bt_ots *ots, struct bt_conn *conn, |
| uint64_t id, void **data, size_t len, |
| off_t offset) |
| { |
| char id_str[BT_OTS_OBJ_ID_STR_LEN]; |
| uint32_t obj_index = ((id - BT_OTS_OBJ_ID_MIN) % ARRAY_SIZE(objects)); |
| bt_ots_obj_id_to_str(id, id_str, sizeof(id_str)); |
| |
| if (!data) { |
| printk("Object with %s ID has been successfully read\n", |
| id_str); |
| |
| return 0; |
| } |
| |
| *data = &objects[obj_index].data[offset]; |
| |
| /* Send even-indexed objects in 20 byte packets |
| * to demonstrate fragmented transmission. |
| */ |
| if ((obj_index % 2) == 0) { |
| len = (len < 20) ? len : 20; |
| } |
| |
| printk("Object with %s ID is being read\n" |
| "Offset = %lu, Length = %zu\n", |
| id_str, (long)offset, len); |
| |
| return len; |
| } |
| |
| static ssize_t ots_obj_write(struct bt_ots *ots, struct bt_conn *conn, |
| uint64_t id, const void *data, size_t len, |
| off_t offset, size_t rem) |
| { |
| char id_str[BT_OTS_OBJ_ID_STR_LEN]; |
| uint32_t obj_index = ((id - BT_OTS_OBJ_ID_MIN) % ARRAY_SIZE(objects)); |
| |
| bt_ots_obj_id_to_str(id, id_str, sizeof(id_str)); |
| |
| printk("Object with %s ID is being written\n" |
| "Offset = %lu, Length = %zu, Remaining= %zu\n", |
| id_str, (long)offset, len, rem); |
| |
| (void)memcpy(&objects[obj_index].data[offset], data, len); |
| |
| return len; |
| } |
| |
| static void ots_obj_name_written(struct bt_ots *ots, struct bt_conn *conn, |
| uint64_t id, const char *cur_name, const char *new_name) |
| { |
| char id_str[BT_OTS_OBJ_ID_STR_LEN]; |
| |
| bt_ots_obj_id_to_str(id, id_str, sizeof(id_str)); |
| |
| printk("Name for object with %s ID is being changed from '%s' to '%s'\n", |
| id_str, cur_name, new_name); |
| } |
| |
| static struct bt_ots_cb ots_callbacks = { |
| .obj_created = ots_obj_created, |
| .obj_deleted = ots_obj_deleted, |
| .obj_selected = ots_obj_selected, |
| .obj_read = ots_obj_read, |
| .obj_write = ots_obj_write, |
| .obj_name_written = ots_obj_name_written, |
| }; |
| |
| static int ots_init(void) |
| { |
| int err; |
| struct bt_ots *ots; |
| struct object_creation_data obj_data; |
| struct bt_ots_init ots_init; |
| struct bt_ots_obj_add_param param; |
| const char * const first_object_name = "first_object.txt"; |
| const char * const second_object_name = "second_object.gif"; |
| uint32_t cur_size; |
| uint32_t alloc_size; |
| |
| ots = bt_ots_free_instance_get(); |
| if (!ots) { |
| printk("Failed to retrieve OTS instance\n"); |
| return -ENOMEM; |
| } |
| |
| /* Configure OTS initialization. */ |
| (void)memset(&ots_init, 0, sizeof(ots_init)); |
| BT_OTS_OACP_SET_FEAT_READ(ots_init.features.oacp); |
| BT_OTS_OACP_SET_FEAT_WRITE(ots_init.features.oacp); |
| BT_OTS_OACP_SET_FEAT_CREATE(ots_init.features.oacp); |
| BT_OTS_OACP_SET_FEAT_DELETE(ots_init.features.oacp); |
| BT_OTS_OACP_SET_FEAT_PATCH(ots_init.features.oacp); |
| BT_OTS_OLCP_SET_FEAT_GO_TO(ots_init.features.olcp); |
| ots_init.cb = &ots_callbacks; |
| |
| /* Initialize OTS instance. */ |
| err = bt_ots_init(ots, &ots_init); |
| if (err) { |
| printk("Failed to init OTS (err:%d)\n", err); |
| return err; |
| } |
| |
| /* Prepare first object demo data and add it to the instance. */ |
| cur_size = sizeof(objects[0].data) / 2; |
| alloc_size = sizeof(objects[0].data); |
| for (uint32_t i = 0; i < cur_size; i++) { |
| objects[0].data[i] = i + 1; |
| } |
| |
| (void)memset(&obj_data, 0, sizeof(obj_data)); |
| __ASSERT(strlen(first_object_name) <= CONFIG_BT_OTS_OBJ_MAX_NAME_LEN, |
| "Object name length is larger than the allowed maximum of %u", |
| CONFIG_BT_OTS_OBJ_MAX_NAME_LEN); |
| (void)strcpy(objects[0].name, first_object_name); |
| obj_data.name = objects[0].name; |
| obj_data.size.cur = cur_size; |
| obj_data.size.alloc = alloc_size; |
| BT_OTS_OBJ_SET_PROP_READ(obj_data.props); |
| BT_OTS_OBJ_SET_PROP_WRITE(obj_data.props); |
| BT_OTS_OBJ_SET_PROP_PATCH(obj_data.props); |
| object_being_created = &obj_data; |
| |
| param.size = alloc_size; |
| param.type.uuid.type = BT_UUID_TYPE_16; |
| param.type.uuid_16.val = BT_UUID_OTS_TYPE_UNSPECIFIED_VAL; |
| err = bt_ots_obj_add(ots, ¶m); |
| object_being_created = NULL; |
| if (err < 0) { |
| printk("Failed to add an object to OTS (err: %d)\n", err); |
| return err; |
| } |
| |
| /* Prepare second object demo data and add it to the instance. */ |
| cur_size = sizeof(objects[0].data); |
| alloc_size = sizeof(objects[0].data); |
| for (uint32_t i = 0; i < cur_size; i++) { |
| objects[1].data[i] = i * 2; |
| } |
| |
| (void)memset(&obj_data, 0, sizeof(obj_data)); |
| __ASSERT(strlen(second_object_name) <= CONFIG_BT_OTS_OBJ_MAX_NAME_LEN, |
| "Object name length is larger than the allowed maximum of %u", |
| CONFIG_BT_OTS_OBJ_MAX_NAME_LEN); |
| (void)strcpy(objects[1].name, second_object_name); |
| obj_data.name = objects[1].name; |
| obj_data.size.cur = cur_size; |
| obj_data.size.alloc = alloc_size; |
| BT_OTS_OBJ_SET_PROP_READ(obj_data.props); |
| object_being_created = &obj_data; |
| |
| param.size = alloc_size; |
| param.type.uuid.type = BT_UUID_TYPE_16; |
| param.type.uuid_16.val = BT_UUID_OTS_TYPE_UNSPECIFIED_VAL; |
| err = bt_ots_obj_add(ots, ¶m); |
| object_being_created = NULL; |
| if (err < 0) { |
| printk("Failed to add an object to OTS (err: %d)\n", err); |
| return err; |
| } |
| |
| return 0; |
| } |
| |
| void main(void) |
| { |
| int err; |
| |
| printk("Starting Bluetooth Peripheral OTS example\n"); |
| |
| err = bt_enable(NULL); |
| if (err) { |
| printk("Bluetooth init failed (err %d)\n", err); |
| return; |
| } |
| |
| printk("Bluetooth initialized\n"); |
| |
| err = ots_init(); |
| if (err) { |
| printk("Failed to init OTS (err:%d)\n", err); |
| return; |
| } |
| |
| err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), |
| sd, ARRAY_SIZE(sd)); |
| if (err) { |
| printk("Advertising failed to start (err %d)\n", err); |
| return; |
| } |
| |
| printk("Advertising successfully started\n"); |
| } |