uint16_t dec_read_blob_req(const uint8_t *pdu, size_t len, uint16_t *handle, uint16_t *offset) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle) + sizeof(*offset); if (pdu == NULL) return 0; if (handle == NULL) return 0; if (offset == NULL) return 0; if (len < min_len) return 0; if (pdu[0] != ATT_OP_READ_BLOB_REQ) return 0; *handle = att_get_u16(&pdu[1]); *offset = att_get_u16(&pdu[3]); return min_len; }
uint16_t dec_read_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start, uint16_t *end, bt_uuid_t *uuid) { const size_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end); if (pdu == NULL) return 0; if (start == NULL || end == NULL || uuid == NULL) return 0; if (len < min_len + 2) return 0; if (pdu[0] != ATT_OP_READ_BY_TYPE_REQ) return 0; *start = att_get_u16(&pdu[1]); *end = att_get_u16(&pdu[3]); if (len == min_len + 2) *uuid = att_get_uuid16(&pdu[5]); else *uuid = att_get_uuid128(&pdu[5]); return len; }
uint16_t dec_find_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start, uint16_t *end, bt_uuid_t *uuid, uint8_t *value, size_t *vlen) { if (pdu == NULL) return 0; if (len < 7) return 0; /* Attribute Opcode (1 octet) */ if (pdu[0] != ATT_OP_FIND_BY_TYPE_REQ) return 0; /* First requested handle number (2 octets) */ *start = att_get_u16(&pdu[1]); /* Last requested handle number (2 octets) */ *end = att_get_u16(&pdu[3]); /* 16-bit UUID to find (2 octets) */ *uuid = att_get_uuid16(&pdu[5]); /* Attribute value to find */ *vlen = len - 7; if (*vlen > 0) memcpy(value, pdu + 7, *vlen); return len; }
GSList *dec_find_by_type_resp(const uint8_t *pdu, size_t len) { struct att_range *range; GSList *matches; off_t offset; /* PDU should contain at least: * - Attribute Opcode (1 octet) * - Handles Information List (at least one entry): * - Found Attribute Handle (2 octets) * - Group End Handle (2 octets) */ if (pdu == NULL || len < 5) return NULL; if (pdu[0] != ATT_OP_FIND_BY_TYPE_RESP) return NULL; /* Reject incomplete Handles Information List */ if ((len - 1) % 4) return NULL; for (offset = 1, matches = NULL; len >= (offset + sizeof(uint16_t) * 2); offset += sizeof(uint16_t) * 2) { range = g_new0(struct att_range, 1); range->start = att_get_u16(&pdu[offset]); range->end = att_get_u16(&pdu[offset + 2]); matches = g_slist_append(matches, range); } return matches; }
uint16_t dec_read_by_grp_req(const uint8_t *pdu, int len, uint16_t *start, uint16_t *end, uuid_t *uuid) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end); if (pdu == NULL) return 0; if (start == NULL || end == NULL || uuid == NULL) return 0; if (pdu[0] != ATT_OP_READ_BY_GROUP_REQ) return 0; if (len < min_len + 2) return 0; *start = att_get_u16(&pdu[1]); *end = att_get_u16(&pdu[3]); if (len == min_len + 2) sdp_uuid16_create(uuid, att_get_u16(&pdu[5])); else sdp_uuid128_create(uuid, &pdu[5]); return len; }
static void read_pnpid_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) { struct characteristic *ch = user_data; uint8_t value[PNP_ID_SIZE]; ssize_t vlen; if (status != 0) { error("Error reading PNP_ID value: %s", att_ecode2str(status)); return; } vlen = dec_read_resp(pdu, len, value, sizeof(value)); if (vlen < 0) { error("Error reading PNP_ID: Protocol error"); return; } if (vlen < 7) { error("Error reading PNP_ID: Invalid pdu length received"); return; } btd_device_set_pnpid(ch->d->dev, value[0], att_get_u16(&value[1]), att_get_u16(&value[3]), att_get_u16(&value[5])); }
uint16_t dec_prep_write_resp(const uint8_t *pdu, size_t len, uint16_t *handle, uint16_t *offset, uint8_t *value, size_t *vlen) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle) + sizeof(*offset); if (pdu == NULL) return 0; if (handle == NULL || offset == NULL || value == NULL || vlen == NULL) return 0; if (len < min_len) return 0; if (pdu[0] != ATT_OP_PREP_WRITE_REQ) return 0; *handle = att_get_u16(&pdu[1]); *offset = att_get_u16(&pdu[3]); *vlen = len - min_len; if (*vlen > 0) memcpy(value, pdu + min_len, *vlen); return len; }
static void discover_ccc_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) { struct heartrate *hr = user_data; struct att_data_list *list; uint8_t format; int i; if (status != 0) { error("Discover Heart Rate Measurement descriptors failed: %s", att_ecode2str(status)); return; } list = dec_find_info_resp(pdu, len, &format); if (list == NULL) return; if (format != ATT_FIND_INFO_RESP_FMT_16BIT) goto done; for (i = 0; i < list->num; i++) { uint8_t *value; uint16_t handle, uuid; char *msg; uint8_t attr_val[2]; value = list->data[i]; handle = att_get_u16(value); uuid = att_get_u16(value + 2); if (uuid != GATT_CLIENT_CHARAC_CFG_UUID) continue; hr->measurement_ccc_handle = handle; if (g_slist_length(hr->hradapter->watchers) == 0) { att_put_u16(0x0000, attr_val); msg = g_strdup("Disable measurement"); } else { att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, attr_val); msg = g_strdup("Enable measurement"); } gatt_write_char(hr->attrib, handle, attr_val, sizeof(attr_val), char_write_cb, msg); break; } done: att_data_list_free(list); }
static void discover_descriptor_cb(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) { struct report *report; struct hog_device *hogdev; struct att_data_list *list; uint8_t format; int i; if (status != 0) { error("Discover all characteristic descriptors failed: %s", att_ecode2str(status)); return; } list = dec_find_info_resp(pdu, len, &format); if (list == NULL) return; if (format != ATT_FIND_INFO_RESP_FMT_16BIT) goto done; for (i = 0; i < list->num; i++) { uint16_t uuid16, handle; uint8_t *value; value = list->data[i]; handle = att_get_u16(value); uuid16 = att_get_u16(&value[2]); switch (uuid16) { case GATT_CLIENT_CHARAC_CFG_UUID: report = user_data; write_ccc(handle, report->hogdev); break; case GATT_REPORT_REFERENCE: report = user_data; gatt_read_char(report->hogdev->attrib, handle, report_reference_cb, report); break; case GATT_EXTERNAL_REPORT_REFERENCE: hogdev = user_data; gatt_read_char(hogdev->attrib, handle, external_report_reference_cb, hogdev); break; } } done: att_data_list_free(list); }
static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data) { uint8_t opdu[ATT_MAX_MTU]; uint16_t handle, i, olen; handle = att_get_u16(&pdu[1]); printf("\r"); switch (pdu[0]) { case ATT_OP_HANDLE_NOTIFY: printf("Notification handle = 0x%04x value: ", handle); break; case ATT_OP_HANDLE_IND: printf("Indication handle = 0x%04x value: ", handle); break; default: printf("Invalid opcode\n"); goto done; } for (i = 3; i < len; i++) printf("%02x ", pdu[i]); if (pdu[0] == ATT_OP_HANDLE_NOTIFY) goto done; olen = enc_confirmation(opdu, sizeof(opdu)); if (olen > 0) g_attrib_send(attrib, 0, opdu[0], opdu, olen, NULL, NULL, NULL); done: printf("\r%s", get_prompt()); fflush(stdout); }
static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data) { GAttrib *attrib = user_data; uint8_t *opdu; uint16_t handle, i, olen = 0; size_t plen; handle = att_get_u16(&pdu[1]); switch (pdu[0]) { case ATT_OP_HANDLE_NOTIFY: g_print("Notification handle = 0x%04x value: ", handle); break; case ATT_OP_HANDLE_IND: g_print("Indication handle = 0x%04x value: ", handle); break; default: g_print("Invalid opcode\n"); return; } for (i = 3; i < len; i++) g_print("%02x ", pdu[i]); g_print("\n"); if (pdu[0] == ATT_OP_HANDLE_NOTIFY) return; opdu = g_attrib_get_buffer(attrib, &plen); olen = enc_confirmation(opdu, plen); if (olen > 0) g_attrib_send(attrib, 0, opdu, olen, NULL, NULL, NULL); }
static void char_read_by_uuid_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { struct att_data_list *list; int i; GString *s; if (status != 0) { error("Read characteristics by UUID failed: %s\n", att_ecode2str(status)); return; } list = dec_read_by_type_resp(pdu, plen); if (list == NULL) return; s = g_string_new(NULL); for (i = 0; i < list->num; i++) { uint8_t *value = list->data[i]; int j; g_string_printf(s, "handle: 0x%04x \t value: ", att_get_u16(value)); value += 2; for (j = 0; j < list->len - 2; j++, value++) g_string_append_printf(s, "%02x ", *value); rl_printf("%s\n", s->str); } att_data_list_free(list); g_string_free(s, TRUE); }
struct attribute *dec_indication(const uint8_t *pdu, int len) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t); struct attribute *a; if (pdu == NULL) return NULL; if (pdu[0] != ATT_OP_HANDLE_IND) return NULL; if (len < min_len) return NULL; a = malloc(sizeof(struct attribute) + len - min_len); if (a == NULL) return NULL; a->len = len - min_len; a->handle = att_get_u16(&pdu[1]); memcpy(a->data, &pdu[3], a->len); return a; }
static void info_read_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { struct hog_device *hogdev = user_data; uint8_t value[HID_INFO_SIZE]; ssize_t vlen; if (status != 0) { error("HID Information read failed: %s", att_ecode2str(status)); return; } vlen = dec_read_resp(pdu, plen, value, sizeof(value)); if (vlen != 4) { error("ATT protocol error"); return; } hogdev->bcdhid = att_get_u16(&value[0]); hogdev->bcountrycode = value[2]; hogdev->flags = value[3]; DBG("bcdHID: 0x%04X bCountryCode: 0x%02X Flags: 0x%02X", hogdev->bcdhid, hogdev->bcountrycode, hogdev->flags); }
static void external_report_reference_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { struct hog_device *hogdev = user_data; uint16_t uuid16; bt_uuid_t uuid; if (status != 0) { error("Read External Report Reference descriptor failed: %s", att_ecode2str(status)); return; } if (plen != 3) { error("Malformed ATT read response"); return; } uuid16 = att_get_u16(&pdu[1]); DBG("External report reference read, external report characteristic " "UUID: 0x%04x", uuid16); bt_uuid16_create(&uuid, uuid16); gatt_discover_char(hogdev->attrib, 0x00, 0xff, &uuid, external_service_char_cb, hogdev); }
static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data) { gatt_connection_t *conn = user_data; uint8_t opdu[ATT_MAX_MTU]; uint16_t handle, i, olen = 0; handle = att_get_u16(&pdu[1]); // printf("0x%02x\n", handle); switch (pdu[0]) { case ATT_OP_HANDLE_NOTIFY: if (conn->notification_handler) { conn->notification_handler(handle, &pdu[3], len, conn->notification_user_data); } break; case ATT_OP_HANDLE_IND: if (conn->indication_handler) { conn->indication_handler(handle, &pdu[3], len, conn->indication_user_data); } break; default: g_print("Invalid opcode\n"); return; } if (pdu[0] == ATT_OP_HANDLE_NOTIFY) return; olen = enc_confirmation(opdu, sizeof(opdu)); if (olen > 0) g_attrib_send(conn->attrib, 0, opdu[0], opdu, olen, NULL, NULL, NULL); }
void read_by_uuid_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { struct att_data_list *list; GSList *bl_value_list = NULL; cb_ctx_t *cb_ctx = user_data; printf_dbg("[CB] IN read_by_uuid_cb\n"); if (status) { cb_ctx->cb_ret_val = BL_REQUEST_FAIL_ERROR; sprintf(cb_ctx->cb_ret_msg, "Read by uuid callback: Failure: %s\n", att_ecode2str(status)); goto error; } list = dec_read_by_type_resp(pdu, plen); if (list == NULL) { strcpy(cb_ctx->cb_ret_msg, "Read by uuid callback: Nothing found\n"); cb_ctx->cb_ret_val = BL_NO_ERROR; goto error; } for (int i = 0; i < list->num; i++) { bl_value_t *bl_value = bl_value_new(NULL, att_get_u16(list->data[i]), list->len - 2, list->data[i] + 2); if (bl_value == NULL) { cb_ctx->cb_ret_val = BL_MALLOC_ERROR; strcpy(cb_ctx->cb_ret_msg, "Read by uuid callback: Malloc error\n"); goto error; } // Add it to the value list if (bl_value_list == NULL) { bl_value_list = g_slist_alloc(); if (bl_value_list == NULL) { cb_ctx->cb_ret_val = BL_MALLOC_ERROR; strcpy(cb_ctx->cb_ret_msg, "Read by uuid callback: Malloc error\n"); goto error; } bl_value_list->data = bl_value; } else { bl_value_list = g_slist_append(bl_value_list, bl_value); } } att_data_list_free(list); cb_ctx->cb_ret_pointer = bl_value_list; cb_ctx->cb_ret_val = BL_NO_ERROR; goto exit; error: if (bl_value_list) bl_value_list_free(bl_value_list); exit: g_mutex_unlock(&cb_ctx->pending_cb_mtx); printf_dbg("[CB] OUT read_by_uuid_cb\n"); }
static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data) { uint8_t opdu[ATT_MAX_MTU]; uint16_t handle, i, olen; handle = att_get_u16(&pdu[1]); printf("\n"); switch (pdu[0]) { case ATT_OP_HANDLE_NOTIFY: printf("Notification handle = 0x%04x value: ", handle); break; case ATT_OP_HANDLE_IND: printf("Indication handle = 0x%04x value: ", handle); break; default: printf("Invalid opcode\n"); return; } for (i = 3; i < len; i++) printf("%02x ", pdu[i]); printf("\n"); rl_forced_update_display(); if (pdu[0] == ATT_OP_HANDLE_NOTIFY) return; olen = enc_confirmation(opdu, sizeof(opdu)); if (olen > 0) g_attrib_send(attrib, 0, opdu[0], opdu, olen, NULL, NULL, NULL); }
static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data) { uint8_t *opdu; uint8_t evt; uint16_t handle, i, olen; size_t plen; evt = pdu[0]; if ( evt != ATT_OP_HANDLE_NOTIFY && evt != ATT_OP_HANDLE_IND ) { printf("#Invalid opcode %02X in event handler??\n", evt); return; } assert( len >= 3 ); handle = att_get_u16(&pdu[1]); resp_begin( evt==ATT_OP_HANDLE_NOTIFY ? rsp_NOTIFY : rsp_IND ); send_uint( tag_HANDLE, handle ); send_data( pdu+3, len-3 ); resp_end(); if (evt == ATT_OP_HANDLE_NOTIFY) return; opdu = g_attrib_get_buffer(attrib, &plen); olen = enc_confirmation(opdu, plen); if (olen > 0) g_attrib_send(attrib, 0, opdu, olen, NULL, NULL, NULL); }
static void char_read_by_uuid_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { struct characteristic_data *char_data = user_data; struct att_data_list *list; int i; if (status == ATT_ECODE_ATTR_NOT_FOUND && char_data->start != opt_start) goto done; if (status != 0) { g_printerr("Read characteristics by UUID failed: %s\n", att_ecode2str(status)); goto done; } list = dec_read_by_type_resp(pdu, plen); if (list == NULL) goto done; for (i = 0; i < list->num; i++) { uint8_t *value = list->data[i]; int j; char_data->start = att_get_u16(value) + 1; g_print("handle: 0x%04x \t value: ", att_get_u16(value)); value += 2; for (j = 0; j < list->len - 2; j++, value++) g_print("%02x ", *value); g_print("\n"); } att_data_list_free(list); gatt_read_char_by_uuid(char_data->attrib, char_data->start, char_data->end, opt_uuid, char_read_by_uuid_cb, char_data); return; done: g_free(char_data); g_main_loop_quit(event_loop); }
static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data) { uint16_t handle = att_get_u16(&pdu[1]); if (pdu[0] == ATT_OP_HANDLE_NOTIFY) { uint16_t handle = att_get_u16(&pdu[1]); if (handle != vehicle.read_char.value_handle) { error("Invalid vehicle read handle: 0x%04x\n", handle); return; } const uint8_t *data = &pdu[3]; const uint16_t datalen = len-3; handle_vehicle_msg_response(data, datalen); return; } }
static struct gatt_included *included_from_buf(const uint8_t *buf, gsize len) { struct gatt_included *incl = g_new0(struct gatt_included, 1); incl->handle = att_get_u16(&buf[0]); incl->range.start = att_get_u16(&buf[2]); incl->range.end = att_get_u16(&buf[4]); if (len == 8) { bt_uuid_t uuid128; bt_uuid_t uuid16 = att_get_uuid16(&buf[6]); bt_uuid_to_uuid128(&uuid16, &uuid128); bt_uuid_to_string(&uuid128, incl->uuid, sizeof(incl->uuid)); } return incl; }
static void char_read_by_uuid_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { struct characteristic_data *char_data = user_data; struct att_data_list *list; int i; if (status == ATT_ECODE_ATTR_NOT_FOUND && char_data->start != char_data->orig_start) { printf("# TODO case in char_read_by_uuid_cb\n"); goto done; } if (status != 0) { resp_error(err_COMM_ERR); // Todo: status goto done; } list = dec_read_by_type_resp(pdu, plen); resp_begin(rsp_READ); if (list == NULL) goto nolist; for (i = 0; i < list->num; i++) { uint8_t *value = list->data[i]; int j; char_data->start = att_get_u16(value) + 1; send_uint(tag_HANDLE, att_get_u16(value)); send_data(value+2, list->len-2); // All the same length?? } att_data_list_free(list); nolist: resp_end(); done: g_free(char_data); }
static void char_read_by_uuid_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { struct characteristic_data *char_data = user_data; struct att_data_list *list; int i; if (status == ATT_ECODE_ATTR_NOT_FOUND && char_data->start != char_data->orig_start) goto done; if (status != 0) { printf("Read characteristics by UUID failed: %s\n", att_ecode2str(status)); goto done; } list = dec_read_by_type_resp(pdu, plen); if (list == NULL) goto done; for (i = 0; i < list->num; i++) { uint8_t *value = list->data[i]; int j; char_data->start = att_get_u16(value) + 1; printf("\nhandle: 0x%04x \t value: ", att_get_u16(value)); value += 2; for (j = 0; j < list->len - 2; j++, value++) printf("%02x ", *value); printf("\n"); } att_data_list_free(list); done: printf("\n%s", get_prompt()); fflush(stdout); g_free(char_data); }
uint16_t dec_find_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start, uint16_t *end, bt_uuid_t *uuid, uint8_t *value, size_t *vlen) { size_t valuelen; uint16_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end) + sizeof(uint16_t); if (pdu == NULL) return 0; if (len < min_len) return 0; if (pdu[0] != ATT_OP_FIND_BY_TYPE_REQ) return 0; /* First requested handle number */ if (start) *start = att_get_u16(&pdu[1]); /* Last requested handle number */ if (end) *end = att_get_u16(&pdu[3]); /* Always UUID16 */ if (uuid) *uuid = att_get_uuid16(&pdu[5]); valuelen = len - min_len; /* Attribute value to find */ if (valuelen > 0 && value) memcpy(value, pdu + min_len, valuelen); if (vlen) *vlen = valuelen; return len; }
uint16_t dec_find_info_req(const uint8_t *pdu, size_t len, uint16_t *start, uint16_t *end) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end); if (pdu == NULL) return 0; if (len < min_len) return 0; if (start == NULL || end == NULL) return 0; if (pdu[0] != ATT_OP_FIND_INFO_REQ) return 0; *start = att_get_u16(&pdu[1]); *end = att_get_u16(&pdu[3]); return min_len; }
GSList *dec_find_by_type_resp(const uint8_t *pdu, int len) { struct att_range *range; GSList *matches; int offset; if (pdu == NULL || len < 5) return NULL; if (pdu[0] != ATT_OP_FIND_BY_TYPE_RESP) return NULL; for (offset = 1, matches = NULL; len >= (offset + 4); offset += 4) { range = g_new0(struct att_range, 1); range->start = att_get_u16(&pdu[offset]); range->end = att_get_u16(&pdu[offset + 2]); matches = g_slist_append(matches, range); } return matches; }
static void report_value_cb(const uint8_t *pdu, uint16_t len, gpointer user_data) { struct hog_device *hogdev = user_data; struct uhid_event ev; uint16_t report_size = len - 3; guint handle; GSList *l; struct report *report; uint8_t *buf; if (len < 3) { /* 1-byte opcode + 2-byte handle */ error("Malformed ATT notification"); return; } handle = att_get_u16(&pdu[1]); l = g_slist_find_custom(hogdev->reports, GUINT_TO_POINTER(handle), report_handle_cmp); if (!l) { error("Invalid report"); return; } report = l->data; memset(&ev, 0, sizeof(ev)); ev.type = UHID_INPUT; ev.u.input.size = MIN(report_size, UHID_DATA_MAX); buf = ev.u.input.data; if (hogdev->prepend_id) { *buf = report->id; buf++; ev.u.input.size++; } memcpy(buf, &pdu[3], MIN(report_size, UHID_DATA_MAX)); if (write(hogdev->uhid_fd, &ev, sizeof(ev)) < 0) error("uHID write failed: %s", strerror(errno)); else DBG("Report from HoG device 0x%04X written to uHID fd %d", hogdev->id, hogdev->uhid_fd); }
static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data) { struct gatt_service *gatt = user_data; struct characteristic *chr; GSList *l; uint8_t opdu[ATT_MAX_MTU]; guint handle; uint16_t olen; if (len < 3) { DBG("Malformed notification/indication packet (opcode 0x%02x)", pdu[0]); return; } handle = att_get_u16(&pdu[1]); l = g_slist_find_custom(gatt->chars, GUINT_TO_POINTER(handle), characteristic_handle_cmp); if (!l) return; chr = l->data; if (chr == NULL) { DBG("Attribute handle 0x%02x not found", handle); return; } switch (pdu[0]) { case ATT_OP_HANDLE_IND: olen = enc_confirmation(opdu, sizeof(opdu)); g_attrib_send(gatt->attrib, 0, opdu[0], opdu, olen, NULL, NULL, NULL); case ATT_OP_HANDLE_NOTIFY: if (characteristic_set_value(chr, &pdu[3], len - 3) < 0) DBG("Can't change Characteristic 0x%02x", handle); g_slist_foreach(gatt->watchers, update_watchers_valuechange, chr); break; } }
static void char_desc_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { struct att_data_list *list; guint8 format; uint16_t handle = 0xffff; int i; if (status != 0) { resp_error(err_COMM_ERR); // Todo: status return; } list = dec_find_info_resp(pdu, plen, &format); if (list == NULL) { resp_error(err_NOT_FOUND); // Todo: what does this mean? return; } resp_begin(rsp_DESCRIPTORS); for (i = 0; i < list->num; i++) { char uuidstr[MAX_LEN_UUID_STR]; uint8_t *value; bt_uuid_t uuid; value = list->data[i]; handle = att_get_u16(value); if (format == 0x01) uuid = att_get_uuid16(&value[2]); else uuid = att_get_uuid128(&value[2]); bt_uuid_to_string(&uuid, uuidstr, MAX_LEN_UUID_STR); send_uint(tag_HANDLE, handle); send_str (tag_UUID, uuidstr); } resp_end(); att_data_list_free(list); if (handle != 0xffff && handle < end) gatt_discover_char_desc(attrib, handle + 1, end, char_desc_cb, NULL); }