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