1 /*
2 * Copyright (c) 2020 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/types.h>
8 #include <string.h>
9 #include <errno.h>
10 #include <zephyr/sys/printk.h>
11 #include <zephyr/kernel.h>
12
13 #include <zephyr/bluetooth/bluetooth.h>
14 #include <zephyr/bluetooth/conn.h>
15 #include <zephyr/bluetooth/gatt.h>
16 #include <zephyr/bluetooth/hci.h>
17
18 #include <zephyr/bluetooth/services/ots.h>
19
20 #define DEVICE_NAME CONFIG_BT_DEVICE_NAME
21 #define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
22
23 #define OBJ_POOL_SIZE CONFIG_BT_OTS_MAX_OBJ_CNT
24 #define OBJ_MAX_SIZE 100
25
26 static const struct bt_data ad[] = {
27 BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
28 BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
29 };
30
31 static const struct bt_data sd[] = {
32 BT_DATA_BYTES(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(BT_UUID_OTS_VAL)),
33 };
34
35 static struct {
36 uint8_t data[OBJ_MAX_SIZE];
37 char name[CONFIG_BT_OTS_OBJ_MAX_NAME_LEN + 1];
38 } objects[OBJ_POOL_SIZE];
39 static uint32_t obj_cnt;
40
41 struct object_creation_data {
42 struct bt_ots_obj_size size;
43 char *name;
44 uint32_t props;
45 };
46
47 #define OTS_OBJ_ID_TO_OBJ_IDX(id) (((id) - BT_OTS_OBJ_ID_MIN) % ARRAY_SIZE(objects))
48
49 static struct object_creation_data *object_being_created;
50
connected(struct bt_conn * conn,uint8_t err)51 static void connected(struct bt_conn *conn, uint8_t err)
52 {
53 if (err) {
54 printk("Connection failed, err %u %s\n", err, bt_hci_err_to_str(err));
55 return;
56 }
57
58 printk("Connected\n");
59 }
60
disconnected(struct bt_conn * conn,uint8_t reason)61 static void disconnected(struct bt_conn *conn, uint8_t reason)
62 {
63 printk("Disconnected, reason %u %s\n", reason, bt_hci_err_to_str(reason));
64 }
65
66 BT_CONN_CB_DEFINE(conn_callbacks) = {
67 .connected = connected,
68 .disconnected = disconnected,
69 };
70
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)71 static int ots_obj_created(struct bt_ots *ots, struct bt_conn *conn, uint64_t id,
72 const struct bt_ots_obj_add_param *add_param,
73 struct bt_ots_obj_created_desc *created_desc)
74 {
75 char id_str[BT_OTS_OBJ_ID_STR_LEN];
76 uint64_t index;
77
78 bt_ots_obj_id_to_str(id, id_str, sizeof(id_str));
79
80 if (obj_cnt >= ARRAY_SIZE(objects)) {
81 printk("No item from Object pool is available for Object "
82 "with %s ID\n", id_str);
83 return -ENOMEM;
84 }
85
86 if (add_param->size > OBJ_MAX_SIZE) {
87 printk("Object pool item is too small for Object with %s ID\n",
88 id_str);
89 return -ENOMEM;
90 }
91
92 if (object_being_created) {
93 created_desc->name = object_being_created->name;
94 created_desc->size = object_being_created->size;
95 created_desc->props = object_being_created->props;
96 } else {
97 index = id - BT_OTS_OBJ_ID_MIN;
98 objects[index].name[0] = '\0';
99
100 created_desc->name = objects[index].name;
101 created_desc->size.alloc = OBJ_MAX_SIZE;
102 BT_OTS_OBJ_SET_PROP_READ(created_desc->props);
103 BT_OTS_OBJ_SET_PROP_WRITE(created_desc->props);
104 BT_OTS_OBJ_SET_PROP_PATCH(created_desc->props);
105 BT_OTS_OBJ_SET_PROP_DELETE(created_desc->props);
106 }
107
108 printk("Object with %s ID has been created\n", id_str);
109 obj_cnt++;
110
111 return 0;
112 }
113
ots_obj_deleted(struct bt_ots * ots,struct bt_conn * conn,uint64_t id)114 static int ots_obj_deleted(struct bt_ots *ots, struct bt_conn *conn,
115 uint64_t id)
116 {
117 char id_str[BT_OTS_OBJ_ID_STR_LEN];
118
119 bt_ots_obj_id_to_str(id, id_str, sizeof(id_str));
120
121 printk("Object with %s ID has been deleted\n", id_str);
122
123 obj_cnt--;
124
125 return 0;
126 }
127
ots_obj_selected(struct bt_ots * ots,struct bt_conn * conn,uint64_t id)128 static void ots_obj_selected(struct bt_ots *ots, struct bt_conn *conn,
129 uint64_t id)
130 {
131 char id_str[BT_OTS_OBJ_ID_STR_LEN];
132
133 bt_ots_obj_id_to_str(id, id_str, sizeof(id_str));
134
135 printk("Object with %s ID has been selected\n", id_str);
136 }
137
ots_obj_read(struct bt_ots * ots,struct bt_conn * conn,uint64_t id,void ** data,size_t len,off_t offset)138 static ssize_t ots_obj_read(struct bt_ots *ots, struct bt_conn *conn,
139 uint64_t id, void **data, size_t len,
140 off_t offset)
141 {
142 char id_str[BT_OTS_OBJ_ID_STR_LEN];
143 uint32_t obj_index = OTS_OBJ_ID_TO_OBJ_IDX(id);
144
145 bt_ots_obj_id_to_str(id, id_str, sizeof(id_str));
146
147 if (!data) {
148 printk("Object with %s ID has been successfully read\n",
149 id_str);
150
151 return 0;
152 }
153
154 *data = &objects[obj_index].data[offset];
155
156 /* Send even-indexed objects in 20 byte packets
157 * to demonstrate fragmented transmission.
158 */
159 if ((obj_index % 2) == 0) {
160 len = (len < 20) ? len : 20;
161 }
162
163 printk("Object with %s ID is being read\n"
164 "Offset = %lu, Length = %zu\n",
165 id_str, (long)offset, len);
166
167 return len;
168 }
169
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)170 static ssize_t ots_obj_write(struct bt_ots *ots, struct bt_conn *conn,
171 uint64_t id, const void *data, size_t len,
172 off_t offset, size_t rem)
173 {
174 char id_str[BT_OTS_OBJ_ID_STR_LEN];
175 uint32_t obj_index = OTS_OBJ_ID_TO_OBJ_IDX(id);
176
177 bt_ots_obj_id_to_str(id, id_str, sizeof(id_str));
178
179 printk("Object with %s ID is being written\n"
180 "Offset = %lu, Length = %zu, Remaining= %zu\n",
181 id_str, (long)offset, len, rem);
182
183 (void)memcpy(&objects[obj_index].data[offset], data, len);
184
185 return len;
186 }
187
ots_obj_name_written(struct bt_ots * ots,struct bt_conn * conn,uint64_t id,const char * cur_name,const char * new_name)188 static void ots_obj_name_written(struct bt_ots *ots, struct bt_conn *conn,
189 uint64_t id, const char *cur_name, const char *new_name)
190 {
191 char id_str[BT_OTS_OBJ_ID_STR_LEN];
192
193 bt_ots_obj_id_to_str(id, id_str, sizeof(id_str));
194
195 printk("Name for object with %s ID is being changed from '%s' to '%s'\n",
196 id_str, cur_name, new_name);
197 }
198
ots_obj_cal_checksum(struct bt_ots * ots,struct bt_conn * conn,uint64_t id,off_t offset,size_t len,void ** data)199 static int ots_obj_cal_checksum(struct bt_ots *ots, struct bt_conn *conn, uint64_t id,
200 off_t offset, size_t len, void **data)
201 {
202 uint32_t obj_index = OTS_OBJ_ID_TO_OBJ_IDX(id);
203
204 if (obj_index >= OBJ_POOL_SIZE) {
205 return -ENOENT;
206 }
207
208 *data = &objects[obj_index].data[offset];
209 return 0;
210 }
211
212 static struct bt_ots_cb ots_callbacks = {
213 .obj_created = ots_obj_created,
214 .obj_deleted = ots_obj_deleted,
215 .obj_selected = ots_obj_selected,
216 .obj_read = ots_obj_read,
217 .obj_write = ots_obj_write,
218 .obj_name_written = ots_obj_name_written,
219 .obj_cal_checksum = ots_obj_cal_checksum,
220 };
221
ots_init(void)222 static int ots_init(void)
223 {
224 int err;
225 struct bt_ots *ots;
226 struct object_creation_data obj_data;
227 struct bt_ots_init_param ots_init;
228 struct bt_ots_obj_add_param param;
229 const char * const first_object_name = "first_object.txt";
230 const char * const second_object_name = "second_object.gif";
231 uint32_t cur_size;
232 uint32_t alloc_size;
233
234 ots = bt_ots_free_instance_get();
235 if (!ots) {
236 printk("Failed to retrieve OTS instance\n");
237 return -ENOMEM;
238 }
239
240 /* Configure OTS initialization. */
241 (void)memset(&ots_init, 0, sizeof(ots_init));
242 BT_OTS_OACP_SET_FEAT_READ(ots_init.features.oacp);
243 BT_OTS_OACP_SET_FEAT_WRITE(ots_init.features.oacp);
244 BT_OTS_OACP_SET_FEAT_CREATE(ots_init.features.oacp);
245 BT_OTS_OACP_SET_FEAT_DELETE(ots_init.features.oacp);
246 BT_OTS_OACP_SET_FEAT_PATCH(ots_init.features.oacp);
247 BT_OTS_OLCP_SET_FEAT_GO_TO(ots_init.features.olcp);
248 ots_init.cb = &ots_callbacks;
249
250 /* Initialize OTS instance. */
251 err = bt_ots_init(ots, &ots_init);
252 if (err) {
253 printk("Failed to init OTS (err:%d)\n", err);
254 return err;
255 }
256
257 /* Prepare first object demo data and add it to the instance. */
258 cur_size = sizeof(objects[0].data) / 2;
259 alloc_size = sizeof(objects[0].data);
260 for (uint32_t i = 0; i < cur_size; i++) {
261 objects[0].data[i] = i + 1;
262 }
263
264 (void)memset(&obj_data, 0, sizeof(obj_data));
265 __ASSERT(strlen(first_object_name) <= CONFIG_BT_OTS_OBJ_MAX_NAME_LEN,
266 "Object name length is larger than the allowed maximum of %u",
267 CONFIG_BT_OTS_OBJ_MAX_NAME_LEN);
268 (void)strcpy(objects[0].name, first_object_name);
269 obj_data.name = objects[0].name;
270 obj_data.size.cur = cur_size;
271 obj_data.size.alloc = alloc_size;
272 BT_OTS_OBJ_SET_PROP_READ(obj_data.props);
273 BT_OTS_OBJ_SET_PROP_WRITE(obj_data.props);
274 BT_OTS_OBJ_SET_PROP_PATCH(obj_data.props);
275 object_being_created = &obj_data;
276
277 param.size = alloc_size;
278 param.type.uuid.type = BT_UUID_TYPE_16;
279 param.type.uuid_16.val = BT_UUID_OTS_TYPE_UNSPECIFIED_VAL;
280 err = bt_ots_obj_add(ots, ¶m);
281 object_being_created = NULL;
282 if (err < 0) {
283 printk("Failed to add an object to OTS (err: %d)\n", err);
284 return err;
285 }
286
287 /* Prepare second object demo data and add it to the instance. */
288 cur_size = sizeof(objects[0].data);
289 alloc_size = sizeof(objects[0].data);
290 for (uint32_t i = 0; i < cur_size; i++) {
291 objects[1].data[i] = i * 2;
292 }
293
294 (void)memset(&obj_data, 0, sizeof(obj_data));
295 __ASSERT(strlen(second_object_name) <= CONFIG_BT_OTS_OBJ_MAX_NAME_LEN,
296 "Object name length is larger than the allowed maximum of %u",
297 CONFIG_BT_OTS_OBJ_MAX_NAME_LEN);
298 (void)strcpy(objects[1].name, second_object_name);
299 obj_data.name = objects[1].name;
300 obj_data.size.cur = cur_size;
301 obj_data.size.alloc = alloc_size;
302 BT_OTS_OBJ_SET_PROP_READ(obj_data.props);
303 object_being_created = &obj_data;
304
305 param.size = alloc_size;
306 param.type.uuid.type = BT_UUID_TYPE_16;
307 param.type.uuid_16.val = BT_UUID_OTS_TYPE_UNSPECIFIED_VAL;
308 err = bt_ots_obj_add(ots, ¶m);
309 object_being_created = NULL;
310 if (err < 0) {
311 printk("Failed to add an object to OTS (err: %d)\n", err);
312 return err;
313 }
314
315 return 0;
316 }
317
main(void)318 int main(void)
319 {
320 int err;
321
322 printk("Starting Bluetooth Peripheral OTS example\n");
323
324 err = bt_enable(NULL);
325 if (err) {
326 printk("Bluetooth init failed (err %d)\n", err);
327 return 0;
328 }
329
330 printk("Bluetooth initialized\n");
331
332 err = ots_init();
333 if (err) {
334 printk("Failed to init OTS (err:%d)\n", err);
335 return 0;
336 }
337
338 err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
339 if (err) {
340 printk("Advertising failed to start (err %d)\n", err);
341 return 0;
342 }
343
344 printk("Advertising successfully started\n");
345 return 0;
346 }
347