Home | History | Annotate | Download | only in attrib
      1 /*
      2  *
      3  *  BlueZ - Bluetooth protocol stack for Linux
      4  *
      5  *  Copyright (C) 2010  Nokia Corporation
      6  *  Copyright (C) 2010  Marcel Holtmann <marcel (at) 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 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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