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, &param);
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, &param);
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