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