• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2010  Nokia Corporation
6  *  Copyright (C) 2010  Marcel Holtmann <marcel@holtmann.org>
7  *
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or
12  *  (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  *
23  */
24 
25 #include <stdint.h>
26 #include <stdlib.h>
27 #include <glib.h>
28 #include <bluetooth/uuid.h>
29 #include <bluetooth/sdp.h>
30 #include <bluetooth/sdp_lib.h>
31 
32 #include "att.h"
33 #include "gattrib.h"
34 #include "gatt.h"
35 
36 struct discover_primary {
37 	GAttrib *attrib;
38 	bt_uuid_t uuid;
39 	GSList *primaries;
40 	gatt_cb_t cb;
41 	void *user_data;
42 };
43 
44 struct discover_char {
45 	GAttrib *attrib;
46 	bt_uuid_t *uuid;
47 	uint16_t end;
48 	GSList *characteristics;
49 	gatt_cb_t cb;
50 	void *user_data;
51 };
52 
discover_primary_free(struct discover_primary * dp)53 static void discover_primary_free(struct discover_primary *dp)
54 {
55 	g_slist_free(dp->primaries);
56 	g_attrib_unref(dp->attrib);
57 	g_free(dp);
58 }
59 
discover_char_free(struct discover_char * dc)60 static void discover_char_free(struct discover_char *dc)
61 {
62 	g_slist_foreach(dc->characteristics, (GFunc) g_free, NULL);
63 	g_slist_free(dc->characteristics);
64 	g_attrib_unref(dc->attrib);
65 	g_free(dc->uuid);
66 	g_free(dc);
67 }
68 
encode_discover_primary(uint16_t start,uint16_t end,bt_uuid_t * uuid,uint8_t * pdu,size_t len)69 static guint16 encode_discover_primary(uint16_t start, uint16_t end,
70 				bt_uuid_t *uuid, uint8_t *pdu, size_t len)
71 {
72 	bt_uuid_t prim;
73 	guint16 plen;
74 
75 	bt_uuid16_create(&prim, GATT_PRIM_SVC_UUID);
76 
77 	if (uuid == NULL) {
78 		/* Discover all primary services */
79 		plen = enc_read_by_grp_req(start, end, &prim, pdu, len);
80 	} else {
81 		uint16_t u16;
82 		uint128_t u128;
83 		const void *value;
84 		int vlen;
85 
86 		/* Discover primary service by service UUID */
87 
88 		if (uuid->type == BT_UUID16) {
89 			u16 = htobs(uuid->value.u16);
90 			value = &u16;
91 			vlen = sizeof(u16);
92 		} else {
93 			htob128(&uuid->value.u128, &u128);
94 			value = &u128;
95 			vlen = sizeof(u128);
96 		}
97 
98 		plen = enc_find_by_type_req(start, end, &prim, value, vlen,
99 								pdu, len);
100 	}
101 
102 	return plen;
103 }
104 
primary_by_uuid_cb(guint8 status,const guint8 * ipdu,guint16 iplen,gpointer user_data)105 static void primary_by_uuid_cb(guint8 status, const guint8 *ipdu,
106 					guint16 iplen, gpointer user_data)
107 
108 {
109 	struct discover_primary *dp = user_data;
110 	GSList *ranges, *last;
111 	struct att_range *range;
112 	uint8_t *buf;
113 	guint16 oplen;
114 	int err = 0, buflen;
115 
116 	if (status) {
117 		err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status;
118 		goto done;
119 	}
120 
121 	ranges = dec_find_by_type_resp(ipdu, iplen);
122 	if (ranges == NULL)
123 		goto done;
124 
125 	dp->primaries = g_slist_concat(dp->primaries, ranges);
126 
127 	last = g_slist_last(ranges);
128 	range = last->data;
129 
130 	if (range->end == 0xffff)
131 		goto done;
132 
133 	buf = g_attrib_get_buffer(dp->attrib, &buflen);
134 	oplen = encode_discover_primary(range->end + 1, 0xffff, &dp->uuid,
135 								buf, buflen);
136 
137 	if (oplen == 0)
138 		goto done;
139 
140 	g_attrib_send(dp->attrib, 0, buf[0], buf, oplen, primary_by_uuid_cb,
141 								dp, NULL);
142 	return;
143 
144 done:
145 	dp->cb(dp->primaries, err, dp->user_data);
146 	discover_primary_free(dp);
147 }
148 
primary_all_cb(guint8 status,const guint8 * ipdu,guint16 iplen,gpointer user_data)149 static void primary_all_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
150 							gpointer user_data)
151 {
152 	struct discover_primary *dp = user_data;
153 	struct att_data_list *list;
154 	unsigned int i, err;
155 	uint16_t start, end;
156 
157 	if (status) {
158 		err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status;
159 		goto done;
160 	}
161 
162 	list = dec_read_by_grp_resp(ipdu, iplen);
163 	if (list == NULL) {
164 		err = ATT_ECODE_IO;
165 		goto done;
166 	}
167 
168 	for (i = 0, end = 0; i < list->num; i++) {
169 		const uint8_t *data = list->data[i];
170 		struct att_primary *primary;
171 		bt_uuid_t uuid;
172 
173 		start = att_get_u16(&data[0]);
174 		end = att_get_u16(&data[2]);
175 
176 		if (list->len == 6) {
177 			bt_uuid_t uuid16 = att_get_uuid16(&data[4]);
178 			bt_uuid_to_uuid128(&uuid16, &uuid);
179 		} else if (list->len == 20) {
180 			uuid = att_get_uuid128(&data[4]);
181 		} else {
182 			/* Skipping invalid data */
183 			continue;
184 		}
185 
186 		primary = g_try_new0(struct att_primary, 1);
187 		if (!primary) {
188 			err = ATT_ECODE_INSUFF_RESOURCES;
189 			goto done;
190 		}
191 		primary->start = start;
192 		primary->end = end;
193 		bt_uuid_to_string(&uuid, primary->uuid, sizeof(primary->uuid));
194 		dp->primaries = g_slist_append(dp->primaries, primary);
195 	}
196 
197 	att_data_list_free(list);
198 	err = 0;
199 
200 	if (end != 0xffff) {
201 		int buflen;
202 		uint8_t *buf = g_attrib_get_buffer(dp->attrib, &buflen);
203 		guint16 oplen = encode_discover_primary(end + 1, 0xffff, NULL,
204 								buf, buflen);
205 
206 		g_attrib_send(dp->attrib, 0, buf[0], buf, oplen, primary_all_cb,
207 								dp, NULL);
208 
209 		return;
210 	}
211 
212 done:
213 	dp->cb(dp->primaries, err, dp->user_data);
214 	discover_primary_free(dp);
215 }
216 
gatt_discover_primary(GAttrib * attrib,bt_uuid_t * uuid,gatt_cb_t func,gpointer user_data)217 guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func,
218 							gpointer user_data)
219 {
220 	struct discover_primary *dp;
221 	int buflen;
222 	uint8_t *buf = g_attrib_get_buffer(attrib, &buflen);
223 	GAttribResultFunc cb;
224 	guint16 plen;
225 
226 	plen = encode_discover_primary(0x0001, 0xffff, uuid, buf, buflen);
227 	if (plen == 0)
228 		return 0;
229 
230 	dp = g_try_new0(struct discover_primary, 1);
231 	if (dp == NULL)
232 		return 0;
233 
234 	dp->attrib = g_attrib_ref(attrib);
235 	dp->cb = func;
236 	dp->user_data = user_data;
237 
238 	if (uuid) {
239 		memcpy(&dp->uuid, uuid, sizeof(bt_uuid_t));
240 		cb = primary_by_uuid_cb;
241 	} else
242 		cb = primary_all_cb;
243 
244 	return g_attrib_send(attrib, 0, buf[0], buf, plen, cb, dp, NULL);
245 }
246 
char_discovered_cb(guint8 status,const guint8 * ipdu,guint16 iplen,gpointer user_data)247 static void char_discovered_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
248 							gpointer user_data)
249 {
250 	struct discover_char *dc = user_data;
251 	struct att_data_list *list;
252 	unsigned int i, err;
253 	int buflen;
254 	uint8_t *buf;
255 	guint16 oplen;
256 	bt_uuid_t uuid;
257 	uint16_t last = 0;
258 
259 	if (status) {
260 		err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status;
261 		goto done;
262 	}
263 
264 	list = dec_read_by_type_resp(ipdu, iplen);
265 	if (list == NULL) {
266 		err = ATT_ECODE_IO;
267 		goto done;
268 	}
269 
270 	for (i = 0; i < list->num; i++) {
271 		uint8_t *value = list->data[i];
272 		struct att_char *chars;
273 		bt_uuid_t uuid;
274 
275 		last = att_get_u16(value);
276 
277 		if (list->len == 7) {
278 			bt_uuid_t uuid16 = att_get_uuid16(&value[5]);
279 			bt_uuid_to_uuid128(&uuid16, &uuid);
280 		} else
281 			uuid = att_get_uuid128(&value[5]);
282 
283 		chars = g_try_new0(struct att_char, 1);
284 		if (!chars) {
285 			err = ATT_ECODE_INSUFF_RESOURCES;
286 			goto done;
287 		}
288 
289 		if (dc->uuid && bt_uuid_cmp(dc->uuid, &uuid))
290 			break;
291 
292 		chars->handle = last;
293 		chars->properties = value[2];
294 		chars->value_handle = att_get_u16(&value[3]);
295 		bt_uuid_to_string(&uuid, chars->uuid, sizeof(chars->uuid));
296 		dc->characteristics = g_slist_append(dc->characteristics,
297 									chars);
298 	}
299 
300 	att_data_list_free(list);
301 	err = 0;
302 
303 	if (last != 0) {
304 		buf = g_attrib_get_buffer(dc->attrib, &buflen);
305 
306 		bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
307 
308 		oplen = enc_read_by_type_req(last + 1, dc->end, &uuid, buf,
309 									buflen);
310 
311 		if (oplen == 0)
312 			return;
313 
314 		g_attrib_send(dc->attrib, 0, buf[0], buf, oplen,
315 						char_discovered_cb, dc, NULL);
316 
317 		return;
318 	}
319 
320 done:
321 	dc->cb(dc->characteristics, err, dc->user_data);
322 	discover_char_free(dc);
323 }
324 
gatt_discover_char(GAttrib * attrib,uint16_t start,uint16_t end,bt_uuid_t * uuid,gatt_cb_t func,gpointer user_data)325 guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end,
326 						bt_uuid_t *uuid, gatt_cb_t func,
327 						gpointer user_data)
328 {
329 	int buflen;
330 	uint8_t *buf = g_attrib_get_buffer(attrib, &buflen);
331 	struct discover_char *dc;
332 	bt_uuid_t type_uuid;
333 	guint16 plen;
334 
335 	bt_uuid16_create(&type_uuid, GATT_CHARAC_UUID);
336 
337 	plen = enc_read_by_type_req(start, end, &type_uuid, buf, buflen);
338 	if (plen == 0)
339 		return 0;
340 
341 	dc = g_try_new0(struct discover_char, 1);
342 	if (dc == NULL)
343 		return 0;
344 
345 	dc->attrib = g_attrib_ref(attrib);
346 	dc->cb = func;
347 	dc->user_data = user_data;
348 	dc->end = end;
349 	dc->uuid = g_memdup(uuid, sizeof(bt_uuid_t));
350 
351 	return g_attrib_send(attrib, 0, buf[0], buf, plen, char_discovered_cb,
352 								dc, NULL);
353 }
354 
gatt_read_char_by_uuid(GAttrib * attrib,uint16_t start,uint16_t end,bt_uuid_t * uuid,GAttribResultFunc func,gpointer user_data)355 guint gatt_read_char_by_uuid(GAttrib *attrib, uint16_t start, uint16_t end,
356 					bt_uuid_t *uuid, GAttribResultFunc func,
357 					gpointer user_data)
358 {
359 	int buflen;
360 	uint8_t *buf = g_attrib_get_buffer(attrib, &buflen);
361 	guint16 plen;
362 
363 	plen = enc_read_by_type_req(start, end, uuid, buf, buflen);
364 	if (plen == 0)
365 		return 0;
366 
367 	return g_attrib_send(attrib, 0, ATT_OP_READ_BY_TYPE_REQ,
368 					buf, plen, func, user_data, NULL);
369 }
370 
371 struct read_long_data {
372 	GAttrib *attrib;
373 	GAttribResultFunc func;
374 	gpointer user_data;
375 	guint8 *buffer;
376 	guint16 size;
377 	guint16 handle;
378 	guint id;
379 	gint ref;
380 };
381 
read_long_destroy(gpointer user_data)382 static void read_long_destroy(gpointer user_data)
383 {
384 	struct read_long_data *long_read = user_data;
385 
386 	if (g_atomic_int_dec_and_test(&long_read->ref) == FALSE)
387 		return;
388 
389 	if (long_read->buffer != NULL)
390 		g_free(long_read->buffer);
391 
392 	g_free(long_read);
393 }
394 
read_blob_helper(guint8 status,const guint8 * rpdu,guint16 rlen,gpointer user_data)395 static void read_blob_helper(guint8 status, const guint8 *rpdu, guint16 rlen,
396 							gpointer user_data)
397 {
398 	struct read_long_data *long_read = user_data;
399 	uint8_t *buf;
400 	int buflen;
401 	guint8 *tmp;
402 	guint16 plen;
403 	guint id;
404 
405 	if (status != 0 || rlen == 1) {
406 		status = 0;
407 		goto done;
408 	}
409 
410 	tmp = g_try_realloc(long_read->buffer, long_read->size + rlen - 1);
411 
412 	if (tmp == NULL) {
413 		status = ATT_ECODE_INSUFF_RESOURCES;
414 		goto done;
415 	}
416 
417 	memcpy(&tmp[long_read->size], &rpdu[1], rlen - 1);
418 	long_read->buffer = tmp;
419 	long_read->size += rlen - 1;
420 
421 	buf = g_attrib_get_buffer(long_read->attrib, &buflen);
422 	if (rlen < buflen)
423 		goto done;
424 
425 	plen = enc_read_blob_req(long_read->handle, long_read->size - 1,
426 								buf, buflen);
427 	id = g_attrib_send(long_read->attrib, long_read->id,
428 				ATT_OP_READ_BLOB_REQ, buf, plen,
429 				read_blob_helper, long_read, read_long_destroy);
430 
431 	if (id != 0) {
432 		g_atomic_int_inc(&long_read->ref);
433 		return;
434 	}
435 
436 	status = ATT_ECODE_IO;
437 
438 done:
439 	long_read->func(status, long_read->buffer, long_read->size,
440 							long_read->user_data);
441 }
442 
read_char_helper(guint8 status,const guint8 * rpdu,guint16 rlen,gpointer user_data)443 static void read_char_helper(guint8 status, const guint8 *rpdu,
444 					guint16 rlen, gpointer user_data)
445 {
446 	struct read_long_data *long_read = user_data;
447 	int buflen;
448 	uint8_t *buf = g_attrib_get_buffer(long_read->attrib, &buflen);
449 	guint16 plen;
450 	guint id;
451 
452 	if (status != 0 || rlen < buflen)
453 		goto done;
454 
455 	long_read->buffer = g_malloc(rlen);
456 
457 	if (long_read->buffer == NULL)
458 		goto done;
459 
460 	memcpy(long_read->buffer, rpdu, rlen);
461 	long_read->size = rlen;
462 
463 	plen = enc_read_blob_req(long_read->handle, rlen - 1, buf, buflen);
464 	id = g_attrib_send(long_read->attrib, long_read->id,
465 			ATT_OP_READ_BLOB_REQ, buf, plen, read_blob_helper,
466 			long_read, read_long_destroy);
467 
468 	if (id != 0) {
469 		g_atomic_int_inc(&long_read->ref);
470 		return;
471 	}
472 
473 	status = ATT_ECODE_IO;
474 
475 done:
476 	long_read->func(status, rpdu, rlen, long_read->user_data);
477 }
478 
gatt_read_char(GAttrib * attrib,uint16_t handle,uint16_t offset,GAttribResultFunc func,gpointer user_data)479 guint gatt_read_char(GAttrib *attrib, uint16_t handle, uint16_t offset,
480 				GAttribResultFunc func, gpointer user_data)
481 {
482 	uint8_t *buf;
483 	int buflen;
484 	guint16 plen;
485 	guint id;
486 	struct read_long_data *long_read;
487 
488 	long_read = g_try_new0(struct read_long_data, 1);
489 
490 	if (long_read == NULL)
491 		return 0;
492 
493 	long_read->attrib = attrib;
494 	long_read->func = func;
495 	long_read->user_data = user_data;
496 	long_read->handle = handle;
497 
498 	buf = g_attrib_get_buffer(attrib, &buflen);
499 	if (offset > 0) {
500 		plen = enc_read_blob_req(long_read->handle, offset, buf,
501 									buflen);
502 		id = g_attrib_send(attrib, 0, ATT_OP_READ_BLOB_REQ, buf, plen,
503 				read_blob_helper, long_read, read_long_destroy);
504 	} else {
505 		plen = enc_read_req(handle, buf, buflen);
506 		id = g_attrib_send(attrib, 0, ATT_OP_READ_REQ, buf, plen,
507 				read_char_helper, long_read, read_long_destroy);
508 	}
509 
510 	if (id == 0)
511 		g_free(long_read);
512 	else {
513 		g_atomic_int_inc(&long_read->ref);
514 		long_read->id = id;
515 	}
516 
517 	return id;
518 }
519 
gatt_write_char(GAttrib * attrib,uint16_t handle,uint8_t * value,int vlen,GAttribResultFunc func,gpointer user_data)520 guint gatt_write_char(GAttrib *attrib, uint16_t handle, uint8_t *value,
521 			int vlen, GAttribResultFunc func, gpointer user_data)
522 {
523 	uint8_t *buf;
524 	int buflen;
525 	guint16 plen;
526 
527 	buf = g_attrib_get_buffer(attrib, &buflen);
528 	if (func)
529 		plen = enc_write_req(handle, value, vlen, buf, buflen);
530 	else
531 		plen = enc_write_cmd(handle, value, vlen, buf, buflen);
532 
533 	return g_attrib_send(attrib, 0, buf[0], buf, plen, func,
534 							user_data, NULL);
535 }
536 
gatt_exchange_mtu(GAttrib * attrib,uint16_t mtu,GAttribResultFunc func,gpointer user_data)537 guint gatt_exchange_mtu(GAttrib *attrib, uint16_t mtu, GAttribResultFunc func,
538 							gpointer user_data)
539 {
540 	uint8_t *buf;
541 	int buflen;
542 	guint16 plen;
543 
544 	buf = g_attrib_get_buffer(attrib, &buflen);
545 	plen = enc_mtu_req(mtu, buf, buflen);
546 	return g_attrib_send(attrib, 0, ATT_OP_MTU_REQ, buf, plen, func,
547 							user_data, NULL);
548 }
549 
gatt_find_info(GAttrib * attrib,uint16_t start,uint16_t end,GAttribResultFunc func,gpointer user_data)550 guint gatt_find_info(GAttrib *attrib, uint16_t start, uint16_t end,
551 				GAttribResultFunc func, gpointer user_data)
552 {
553 	uint8_t *buf;
554 	int buflen;
555 	guint16 plen;
556 
557 	buf = g_attrib_get_buffer(attrib, &buflen);
558 	plen = enc_find_info_req(start, end, buf, buflen);
559 	if (plen == 0)
560 		return 0;
561 
562 	return g_attrib_send(attrib, 0, ATT_OP_FIND_INFO_REQ, buf, plen, func,
563 							user_data, NULL);
564 }
565 
gatt_write_cmd(GAttrib * attrib,uint16_t handle,uint8_t * value,int vlen,GDestroyNotify notify,gpointer user_data)566 guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, uint8_t *value, int vlen,
567 				GDestroyNotify notify, gpointer user_data)
568 {
569 	uint8_t *buf;
570 	int buflen;
571 	guint16 plen;
572 
573 	buf = g_attrib_get_buffer(attrib, &buflen);
574 	plen = enc_write_cmd(handle, value, vlen, buf, buflen);
575 	return g_attrib_send(attrib, 0, ATT_OP_WRITE_CMD, buf, plen, NULL,
576 							user_data, notify);
577 }
578 
proto_seq_find(sdp_list_t * proto_list)579 static sdp_data_t *proto_seq_find(sdp_list_t *proto_list)
580 {
581 	sdp_list_t *list;
582 	uuid_t proto;
583 
584 	sdp_uuid16_create(&proto, ATT_UUID);
585 
586 	for (list = proto_list; list; list = list->next) {
587 		sdp_list_t *p;
588 		for (p = list->data; p; p = p->next) {
589 			sdp_data_t *seq = p->data;
590 			if (seq && seq->dtd == SDP_UUID16 &&
591 				sdp_uuid16_cmp(&proto, &seq->val.uuid) == 0)
592 				return seq->next;
593 		}
594 	}
595 
596 	return NULL;
597 }
598 
parse_proto_params(sdp_list_t * proto_list,uint16_t * psm,uint16_t * start,uint16_t * end)599 static gboolean parse_proto_params(sdp_list_t *proto_list, uint16_t *psm,
600 						uint16_t *start, uint16_t *end)
601 {
602 	sdp_data_t *seq1, *seq2;
603 
604 	if (psm)
605 		*psm = sdp_get_proto_port(proto_list, L2CAP_UUID);
606 
607 	/* Getting start and end handle */
608 	seq1 = proto_seq_find(proto_list);
609 	if (!seq1 || seq1->dtd != SDP_UINT16)
610 		return FALSE;
611 
612 	seq2 = seq1->next;
613 	if (!seq2 || seq2->dtd != SDP_UINT16)
614 		return FALSE;
615 
616 	if (start)
617 		*start = seq1->val.uint16;
618 
619 	if (end)
620 		*end = seq2->val.uint16;
621 
622 	return TRUE;
623 }
624 
gatt_parse_record(const sdp_record_t * rec,uuid_t * prim_uuid,uint16_t * psm,uint16_t * start,uint16_t * end)625 gboolean gatt_parse_record(const sdp_record_t *rec,
626 					uuid_t *prim_uuid, uint16_t *psm,
627 					uint16_t *start, uint16_t *end)
628 {
629 	sdp_list_t *list;
630 	uuid_t uuid;
631 	gboolean ret;
632 
633 	if (sdp_get_service_classes(rec, &list) < 0)
634 		return FALSE;
635 
636 	memcpy(&uuid, list->data, sizeof(uuid));
637 	sdp_list_free(list, free);
638 
639 	if (sdp_get_access_protos(rec, &list) < 0)
640 		return FALSE;
641 
642 	ret = parse_proto_params(list, psm, start, end);
643 
644 	sdp_list_foreach(list, (sdp_list_func_t) sdp_list_free, NULL);
645 	sdp_list_free(list, NULL);
646 
647 	/* FIXME: replace by bt_uuid_t after uuid_t/sdp code cleanup */
648 	if (ret && prim_uuid)
649 		memcpy(prim_uuid, &uuid, sizeof(uuid_t));
650 
651 	return ret;
652 }
653