static uint8_t read_group_cb(const struct bt_gatt_attr *attr, void *user_data) { struct read_group_data *data = user_data; struct bt_att *att = data->att; struct bt_conn *conn = att->chan.conn; int read; /* Update group end_handle if attribute is not a service */ if (bt_uuid_cmp(attr->uuid, BT_UUID_GATT_PRIMARY) && bt_uuid_cmp(attr->uuid, BT_UUID_GATT_SECONDARY)) { if (data->group && attr->handle > data->group->end_handle) { data->group->end_handle = sys_cpu_to_le16(attr->handle); } return BT_GATT_ITER_CONTINUE; } /* If Group Type don't match skip */ if (bt_uuid_cmp(attr->uuid, data->uuid)) { data->group = NULL; return BT_GATT_ITER_CONTINUE; } BT_DBG("handle 0x%04x", attr->handle); /* Stop if there is no space left */ if (data->rsp->len && att->chan.tx.mtu - data->buf->len < data->rsp->len) { return BT_GATT_ITER_STOP; } /* Fast foward to next group position */ data->group = net_buf_add(data->buf, sizeof(*data->group)); /* Initialize group handle range */ data->group->start_handle = sys_cpu_to_le16(attr->handle); data->group->end_handle = sys_cpu_to_le16(attr->handle); /* Read attribute value and store in the buffer */ read = attr->read(conn, attr, data->buf->data + data->buf->len, att->chan.tx.mtu - data->buf->len, 0); if (read < 0) { /* TODO: Handle read errors */ return BT_GATT_ITER_STOP; } if (!data->rsp->len) { /* Set len to be the first group found */ data->rsp->len = read + sizeof(*data->group); } else if (data->rsp->len != read + sizeof(*data->group)) { /* All groups entries should have the same size */ data->buf->len -= sizeof(*data->group); return false; } net_buf_add(data->buf, read); /* Continue to find the end handle */ return BT_GATT_ITER_CONTINUE; }
static uint8_t find_type_cb(const struct bt_gatt_attr *attr, void *user_data) { struct find_type_data *data = user_data; struct bt_att *att = data->att; struct bt_conn *conn = att->chan.conn; int read; uint8_t uuid[16]; /* Skip secondary services */ if (!bt_uuid_cmp(attr->uuid, BT_UUID_GATT_SECONDARY)) { data->group = NULL; return BT_GATT_ITER_CONTINUE; } /* Update group end_handle if not a primary service */ if (bt_uuid_cmp(attr->uuid, BT_UUID_GATT_PRIMARY)) { if (data->group && attr->handle > data->group->end_handle) { data->group->end_handle = sys_cpu_to_le16(attr->handle); } return BT_GATT_ITER_CONTINUE; } BT_DBG("handle 0x%04x", attr->handle); /* stop if there is no space left */ if (att->chan.tx.mtu - data->buf->len < sizeof(*data->group)) { return BT_GATT_ITER_STOP; } /* Read attribute value and store in the buffer */ read = attr->read(conn, attr, uuid, sizeof(uuid), 0); if (read < 0) { /* * Since we don't know if it is the service with requested UUID, * we cannot respond with an error to this request. */ data->group = NULL; return BT_GATT_ITER_CONTINUE; } /* Check if data matches */ if (read != data->value_len || memcmp(data->value, uuid, read)) { data->group = NULL; return BT_GATT_ITER_CONTINUE; } /* If service has been found, error should be cleared */ data->err = 0x00; /* Fast foward to next item position */ data->group = net_buf_add(data->buf, sizeof(*data->group)); data->group->start_handle = sys_cpu_to_le16(attr->handle); data->group->end_handle = sys_cpu_to_le16(attr->handle); /* continue to find the end_handle */ return BT_GATT_ITER_CONTINUE; }
uint8_t BLECharacteristicImp::discoverResponseProc(bt_conn_t *conn, const bt_gatt_attr_t *attr, bt_gatt_discover_params_t *params) { const bt_addr_le_t* dst_addr = bt_conn_get_dst(conn); BLEDevice device(dst_addr); uint8_t retVal = BT_GATT_ITER_STOP; pr_debug(LOG_MODULE_BLE, "%s-%d: type-%d", __FUNCTION__, __LINE__, params->type); // Process the service switch (params->type) { case BT_GATT_DISCOVER_DESCRIPTOR: { if (NULL != attr) { retVal = BT_GATT_ITER_CONTINUE; const bt_uuid_t* desc_uuid = attr->uuid; uint16_t desc_handle = attr->handle; pr_debug(LOG_MODULE_BLE, "%s-%d:handle-%d:%d", __FUNCTION__, __LINE__,attr->handle, desc_handle); if (isClientCharacteristicConfigurationDescriptor(desc_uuid)) { setCCCDHandle(desc_handle); } else if (bt_uuid_cmp(BLEServiceImp::getPrimayUuid(), desc_uuid) == 0 || bt_uuid_cmp(getCharacteristicAttributeUuid(), desc_uuid) == 0 ) { retVal = BT_GATT_ITER_STOP; } else { int retval = (int)addDescriptor(desc_uuid, attr->perm, desc_handle); if (BLE_STATUS_SUCCESS != retval) { pr_error(LOG_MODULE_BLE, "%s-%d: Error-%d", __FUNCTION__, __LINE__, retval); errno = ENOMEM; retVal = BT_GATT_ITER_STOP; } } } break; } default: { break; } } return retVal; }
static uint8_t att_read_group_req(struct bt_att *att, struct net_buf *buf) { struct bt_conn *conn = att->chan.conn; struct bt_att_read_group_req *req; uint16_t start_handle, end_handle, err_handle; union { struct bt_uuid uuid; struct bt_uuid_16 u16; struct bt_uuid_128 u128; } u; /* Type can only be UUID16 or UUID128 */ if (buf->len != sizeof(*req) + 2 && buf->len != sizeof(*req) + 16) { return BT_ATT_ERR_INVALID_PDU; } req = (void *)buf->data; start_handle = sys_le16_to_cpu(req->start_handle); end_handle = sys_le16_to_cpu(req->end_handle); net_buf_pull(buf, sizeof(*req)); if (!uuid_create(&u.uuid, buf)) { return BT_ATT_ERR_UNLIKELY; } BT_DBG("start_handle 0x%04x end_handle 0x%04x type %s", start_handle, end_handle, bt_uuid_str(&u.uuid)); if (!range_is_valid(start_handle, end_handle, &err_handle)) { send_err_rsp(conn, BT_ATT_OP_READ_GROUP_REQ, err_handle, BT_ATT_ERR_INVALID_HANDLE); return 0; } /* Core v4.2, Vol 3, sec 2.5.3 Attribute Grouping: * Not all of the grouping attributes can be used in the ATT * Read By Group Type Request. The «Primary Service» and «Secondary * Service» grouping types may be used in the Read By Group Type * Request. The «Characteristic» grouping type shall not be used in * the ATT Read By Group Type Request. */ if (bt_uuid_cmp(&u.uuid, BT_UUID_GATT_PRIMARY) && bt_uuid_cmp(&u.uuid, BT_UUID_GATT_SECONDARY)) { send_err_rsp(conn, BT_ATT_OP_READ_GROUP_REQ, start_handle, BT_ATT_ERR_UNSUPPORTED_GROUP_TYPE); return 0; } return att_read_group_rsp(att, &u.uuid, start_handle, end_handle); }
static void read_by_type(void *data, void *user_data) { struct read_by_type_data *search_data = user_data; struct gatt_db_service *service = data; struct gatt_db_attribute *attribute; int i; if (!service->active) return; for (i = 0; i < service->num_handles; i++) { attribute = service->attributes[i]; if (!attribute) continue; if (attribute->handle < search_data->start_handle) continue; if (attribute->handle > search_data->end_handle) return; if (bt_uuid_cmp(&search_data->uuid, &attribute->uuid)) continue; queue_push_tail(search_data->queue, attribute); } }
static void find_by_type(void *data, void *user_data) { struct find_by_type_value_data *search_data = user_data; struct gatt_db_service *service = data; struct gatt_db_attribute *attribute; int i; if (!service->active) return; for (i = 0; i < service->num_handles; i++) { attribute = service->attributes[i]; if (!attribute) continue; if ((attribute->handle < search_data->start_handle) || (attribute->handle > search_data->end_handle)) continue; if (bt_uuid_cmp(&search_data->uuid, &attribute->uuid)) continue; /* TODO: fix for read-callback based attributes */ if (search_data->value && memcmp(attribute->value, search_data->value, search_data->value_len)) continue; search_data->num_of_res++; search_data->func(attribute, search_data->user_data); } }
bool gatt_db_attribute_get_incl_data(const struct gatt_db_attribute *attrib, uint16_t *handle, uint16_t *start_handle, uint16_t *end_handle) { if (!attrib) return false; if (bt_uuid_cmp(&included_service_uuid, &attrib->uuid)) return false; /* * Include definition value: * 2 octets: start handle of included service * 2 octets: end handle of included service * optional 2 octets: 16-bit Bluetooth UUID */ if (!attrib->value || attrib->value_len < 4 || attrib->value_len > 6) return false; /* * We only return the handles since the UUID can be easily obtained * from the corresponding attribute. */ if (handle) *handle = attrib->handle; if (start_handle) *start_handle = get_le16(attrib->value); if (end_handle) *end_handle = get_le16(attrib->value + 2); return true; }
static uint8_t get_char_extended_prop(const struct gatt_db_attribute *attrib) { uint16_t ext_prop; if (!attrib) return 0; if (bt_uuid_cmp(&characteristic_uuid, &attrib->uuid)) return 0; /* Check properties first */ if (!(attrib->value[0] & BT_GATT_CHRC_PROP_EXT_PROP)) return 0; ext_prop = 0; /* * Cast needed for foreach function. We do not change attrib during * this call */ gatt_db_service_foreach_desc((struct gatt_db_attribute *) attrib, read_ext_prop, &ext_prop); return ext_prop; }
void gatt_db_service_foreach(struct gatt_db_attribute *attrib, const bt_uuid_t *uuid, gatt_db_attribute_cb_t func, void *user_data) { struct gatt_db_service *service; struct gatt_db_attribute *attr; uint16_t i; if (!attrib || !func) return; service = attrib->service; for (i = 0; i < service->num_handles; i++) { attr = service->attributes[i]; if (!attr) continue; if (uuid && bt_uuid_cmp(uuid, &attr->uuid)) continue; func(attr, user_data); } }
bool gatt_db_attribute_get_service_data(const struct gatt_db_attribute *attrib, uint16_t *start_handle, uint16_t *end_handle, bool *primary, bt_uuid_t *uuid) { struct gatt_db_service *service; struct gatt_db_attribute *decl; if (!attrib) return false; service = attrib->service; decl = service->attributes[0]; gatt_db_service_get_handles(service, start_handle, end_handle); if (primary) *primary = bt_uuid_cmp(&decl->uuid, &secondary_service_uuid); if (!uuid) return true; /* * The service declaration attribute value is the 16 or 128 bit service * UUID. */ return le_to_uuid(decl->value, decl->value_len, uuid); }
static bool uuid_cmp(const bt_uuid_t *uuid, uint16_t u16) { bt_uuid_t uuid16; bt_uuid16_create(&uuid16, u16); return bt_uuid_cmp(uuid, &uuid16) == 0; }
static int uuid_desc16_cmp(bt_uuid_t *uuid, guint16 desc) { bt_uuid_t u16; bt_uuid16_create(&u16, desc); return bt_uuid_cmp(uuid, &u16); }
static bool uuid_cmp(uint16_t u16, const bt_uuid_t *uuid) { bt_uuid_t lhs; bt_uuid16_create(&lhs, u16); return bt_uuid_cmp(&lhs, uuid) == 0; }
static uint8_t notify_cb(const struct bt_gatt_attr *attr, void *user_data) { struct notify_data *data = user_data; struct _bt_gatt_ccc *ccc; size_t i; if (bt_uuid_cmp(attr->uuid, BT_UUID_GATT_CCC)) { /* Stop if we reach the next characteristic */ if (!bt_uuid_cmp(attr->uuid, BT_UUID_GATT_CHRC)) { return BT_GATT_ITER_STOP; } return BT_GATT_ITER_CONTINUE; } /* Check attribute user_data must be of type struct _bt_gatt_ccc */ if (attr->write != bt_gatt_attr_write_ccc) { return BT_GATT_ITER_CONTINUE; } ccc = attr->user_data; /* Notify all peers configured */ for (i = 0; i < ccc->cfg_len; i++) { struct bt_conn *conn; /* TODO: Handle indications */ if (ccc->value != BT_GATT_CCC_NOTIFY) { continue; } conn = bt_conn_lookup_addr_le(&ccc->cfg[i].peer); if (!conn || conn->state != BT_CONN_CONNECTED) { continue; } if (att_notify(conn, data->handle, data->data, data->len) < 0) { bt_conn_unref(conn); return BT_GATT_ITER_STOP; } bt_conn_unref(conn); } return BT_GATT_ITER_CONTINUE; }
int bt_uuid_strcmp(const void *a, const void *b) { bt_uuid_t u1, u2; bt_string_to_uuid(&u1, a); bt_string_to_uuid(&u2, b); return bt_uuid_cmp(&u1, &u2); }
static bool find_service_with_uuid(const void *data, const void *user_data) { const struct gatt_db_service *service = data; const bt_uuid_t *uuid = user_data; bt_uuid_t svc_uuid; gatt_db_attribute_get_service_uuid(service->attributes[0], &svc_uuid); return bt_uuid_cmp(uuid, &svc_uuid) == 0; }
void gatt_db_service_foreach_desc(struct gatt_db_attribute *attrib, gatt_db_attribute_cb_t func, void *user_data) { struct gatt_db_service *service; struct gatt_db_attribute *attr; uint16_t i; if (!attrib || !func) return; /* Return if this attribute is not a characteristic declaration */ if (bt_uuid_cmp(&characteristic_uuid, &attrib->uuid)) return; service = attrib->service; /* Start from the attribute following the value handle */ for (i = 0; i < service->num_handles; i++) { if (service->attributes[i] == attrib) { i += 2; break; } } for (; i < service->num_handles; i++) { attr = service->attributes[i]; if (!attr) continue; /* Return if we reached the end of this characteristic */ if (!bt_uuid_cmp(&characteristic_uuid, &attr->uuid) || !bt_uuid_cmp(&included_service_uuid, &attr->uuid)) return; func(attr, user_data); } }
static void char_discovered_cb(uint8_t status, GSList *chars, void *user_data) { struct hog_device *hogdev = user_data; struct gatt_primary *prim = hogdev->hog_primary; bt_uuid_t report_uuid, report_map_uuid, info_uuid; bt_uuid_t proto_mode_uuid, ctrlpt_uuid; struct report *report; GSList *l; uint16_t info_handle = 0, proto_mode_handle = 0; if (status != 0) { const char *str = att_ecode2str(status); DBG("Discover all characteristics failed: %s", str); return; } bt_uuid16_create(&report_uuid, HOG_REPORT_UUID); bt_uuid16_create(&report_map_uuid, HOG_REPORT_MAP_UUID); bt_uuid16_create(&info_uuid, HOG_INFO_UUID); bt_uuid16_create(&proto_mode_uuid, HOG_PROTO_MODE_UUID); bt_uuid16_create(&ctrlpt_uuid, HOG_CONTROL_POINT_UUID); for (l = chars; l; l = g_slist_next(l)) { struct gatt_char *chr, *next; bt_uuid_t uuid; uint16_t start, end; chr = l->data; next = l->next ? l->next->data : NULL; DBG("0x%04x UUID: %s properties: %02x", chr->handle, chr->uuid, chr->properties); bt_string_to_uuid(&uuid, chr->uuid); start = chr->value_handle + 1; end = (next ? next->handle - 1 : prim->range.end); if (bt_uuid_cmp(&uuid, &report_uuid) == 0) { report = g_new0(struct report, 1); report->hogdev = hogdev; report->decl = g_memdup(chr, sizeof(*chr)); hogdev->reports = g_slist_append(hogdev->reports, report); discover_descriptor(hogdev->attrib, start, end, report); } else if (bt_uuid_cmp(&uuid, &report_map_uuid) == 0) {
static void read_ext_prop(struct gatt_db_attribute *attrib, void *user_data) { uint16_t *ext_prop = user_data; /* * If ext_prop is set that means extended properties descriptor * has been already found */ if (*ext_prop != 0) return; if (bt_uuid_cmp(&ext_desc_uuid, &attrib->uuid)) return; gatt_db_attribute_read(attrib, 0, BT_ATT_OP_READ_REQ, NULL, read_ext_prop_value, ext_prop); }
void gatt_db_read_by_group_type(struct gatt_db *db, uint16_t start_handle, uint16_t end_handle, const bt_uuid_t type, struct queue *queue) { const struct queue_entry *services_entry; struct gatt_db_service *service; uint16_t grp_start, grp_end, uuid_size; uuid_size = 0; services_entry = queue_get_entries(db->services); while (services_entry) { service = services_entry->data; if (!service->active) goto next_service; if (bt_uuid_cmp(&type, &service->attributes[0]->uuid)) goto next_service; grp_start = service->attributes[0]->handle; grp_end = grp_start + service->num_handles - 1; if (grp_end < start_handle || grp_start > end_handle) goto next_service; if (grp_start < start_handle || grp_start > end_handle) goto next_service; if (!uuid_size) uuid_size = service->attributes[0]->value_len; else if (uuid_size != service->attributes[0]->value_len) return; queue_push_tail(queue, service->attributes[0]); next_service: services_entry = services_entry->next; } }
static void foreach_service_in_range(void *data, void *user_data) { struct gatt_db_service *service = data; struct foreach_data *foreach_data = user_data; uint16_t svc_start; bt_uuid_t uuid; svc_start = get_handle_at_index(service, 0); if (svc_start > foreach_data->end || svc_start < foreach_data->start) return; if (foreach_data->uuid) { gatt_db_attribute_get_service_uuid(service->attributes[0], &uuid); if (bt_uuid_cmp(&uuid, foreach_data->uuid)) return; } foreach_data->func(service->attributes[0], foreach_data->user_data); }
bool gatt_db_attribute_get_char_data(const struct gatt_db_attribute *attrib, uint16_t *handle, uint16_t *value_handle, uint8_t *properties, uint16_t *ext_prop, bt_uuid_t *uuid) { if (!attrib) return false; if (bt_uuid_cmp(&characteristic_uuid, &attrib->uuid)) return false; /* * Characteristic declaration value: * 1 octet: Characteristic properties * 2 octets: Characteristic value handle * 2 or 16 octets: characteristic UUID */ if (!attrib->value || (attrib->value_len != 5 && attrib->value_len != 19)) return false; if (handle) *handle = attrib->handle; if (properties) *properties = attrib->value[0]; if (ext_prop) *ext_prop = get_char_extended_prop(attrib); if (value_handle) *value_handle = get_le16(attrib->value + 1); if (!uuid) return true; return le_to_uuid(attrib->value + 3, attrib->value_len - 3, uuid); }
static struct network_server *find_server_by_uuid(GSList *list, const char *uuid) { bt_uuid_t srv_uuid, bnep_uuid; if (!bt_string_to_uuid(&srv_uuid, uuid)) { for (; list; list = list->next) { struct network_server *ns = list->data; bt_uuid16_create(&bnep_uuid, ns->id); /* UUID value compare */ if (!bt_uuid_cmp(&srv_uuid, &bnep_uuid)) return ns; } } else { for (; list; list = list->next) { struct network_server *ns = list->data; /* String value compare */ switch (ns->id) { case BNEP_SVC_PANU: if (!strcasecmp(uuid, "panu")) return ns; break; case BNEP_SVC_NAP: if (!strcasecmp(uuid, "nap")) return ns; break; case BNEP_SVC_GN: if (!strcasecmp(uuid, "gn")) return ns; break; } } } return NULL; }
static void service_added(struct gatt_db_attribute *attr, void *user_data) { struct gas *gas = user_data; bt_uuid_t uuid, gap_uuid; if (!bt_gatt_client_is_ready(gas->client)) return; gatt_db_attribute_get_service_uuid(attr, &uuid); bt_uuid16_create(&gap_uuid, GAP_UUID16); if (bt_uuid_cmp(&uuid, &gap_uuid)) return; if (gas->attr) { error("More than one GAP service added to device"); return; } DBG("GAP service added"); gas->attr = attr; handle_gap_service(gas); }
static void handle_characteristic(struct gatt_db_attribute *attr, void *user_data) { struct btd_device *device = user_data; uint16_t value_handle; bt_uuid_t uuid, pnpid_uuid; bt_string_to_uuid(&pnpid_uuid, PNPID_UUID); if (!gatt_db_attribute_get_char_data(attr, NULL, &value_handle, NULL, NULL, &uuid)) { error("Failed to obtain characteristic data"); return; } if (bt_uuid_cmp(&pnpid_uuid, &uuid) == 0) handle_pnpid(device, value_handle); else { char uuid_str[MAX_LEN_UUID_STR]; bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str)); DBG("Unsupported characteristic: %s", uuid_str); } }
static void char_discovered_cb(guint8 status, const guint8 *ipdu, guint16 iplen, gpointer user_data) { struct discover_char *dc = user_data; struct att_data_list *list; unsigned int i, err = ATT_ECODE_ATTR_NOT_FOUND; uint16_t last = 0; if (status) { err = status; goto done; } list = dec_read_by_type_resp(ipdu, iplen); if (list == NULL) { err = ATT_ECODE_IO; goto done; } for (i = 0; i < list->num; i++) { uint8_t *value = list->data[i]; struct gatt_char *chars; bt_uuid_t uuid; last = att_get_u16(value); if (list->len == 7) { bt_uuid_t uuid16 = att_get_uuid16(&value[5]); bt_uuid_to_uuid128(&uuid16, &uuid); } else uuid = att_get_uuid128(&value[5]); if (dc->uuid && bt_uuid_cmp(dc->uuid, &uuid)) continue; chars = g_try_new0(struct gatt_char, 1); if (!chars) { err = ATT_ECODE_INSUFF_RESOURCES; goto done; } chars->handle = last; chars->properties = value[2]; chars->value_handle = att_get_u16(&value[3]); bt_uuid_to_string(&uuid, chars->uuid, sizeof(chars->uuid)); dc->characteristics = g_slist_append(dc->characteristics, chars); } att_data_list_free(list); if (last != 0 && (last + 1 < dc->end)) { bt_uuid_t uuid; guint16 oplen; size_t buflen; uint8_t *buf; buf = g_attrib_get_buffer(dc->attrib, &buflen); bt_uuid16_create(&uuid, GATT_CHARAC_UUID); oplen = enc_read_by_type_req(last + 1, dc->end, &uuid, buf, buflen); if (oplen == 0) return; g_attrib_send(dc->attrib, 0, buf, oplen, char_discovered_cb, dc, NULL); return; } done: err = (dc->characteristics ? 0 : err); dc->cb(dc->characteristics, err, dc->user_data); discover_char_free(dc); }
static void read_by_grp_type_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; uint16_t start, end; bt_uuid_t type; bt_uuid_t prim, snd; uint16_t mtu = bt_att_get_mtu(server->att); uint8_t rsp_pdu[mtu]; uint16_t rsp_len; uint8_t ecode = 0; uint16_t ehandle = 0; struct queue *q = NULL; if (length != 6 && length != 20) { ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } q = queue_new(); if (!q) { ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES; goto error; } start = get_le16(pdu); end = get_le16(pdu + 2); get_uuid_le(pdu + 4, length - 4, &type); util_debug(server->debug_callback, server->debug_data, "Read By Grp Type - start: 0x%04x end: 0x%04x", start, end); if (!start || !end) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } ehandle = start; if (start > end) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } /* * GATT defines that only the <<Primary Service>> and * <<Secondary Service>> group types can be used for the * "Read By Group Type" request (Core v4.1, Vol 3, sec 2.5.3). Return an * error if any other group type is given. */ bt_uuid16_create(&prim, GATT_PRIM_SVC_UUID); bt_uuid16_create(&snd, GATT_SND_SVC_UUID); if (bt_uuid_cmp(&type, &prim) && bt_uuid_cmp(&type, &snd)) { ecode = BT_ATT_ERROR_UNSUPPORTED_GROUP_TYPE; goto error; } gatt_db_read_by_group_type(server->db, start, end, type, q); if (queue_isempty(q)) { ecode = BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND; goto error; } if (!encode_read_by_grp_type_rsp(server->db, q, server->att, mtu, rsp_pdu, &rsp_len)) { ecode = BT_ATT_ERROR_UNLIKELY; goto error; } queue_destroy(q, NULL); bt_att_send(server->att, BT_ATT_OP_READ_BY_GRP_TYPE_RSP, rsp_pdu, rsp_len, NULL, NULL, NULL); return; error: queue_destroy(q, NULL); bt_att_send_error_rsp(server->att, opcode, ehandle, ecode); }
static void char_discovered_cb(guint8 status, const guint8 *ipdu, guint16 iplen, gpointer user_data) { struct discover_char *dc = user_data; struct att_data_list *list; unsigned int i, err = 0; uint16_t last = 0; uint8_t type; /* We have all the characteristic now, lets send it up */ if (status == ATT_ECODE_ATTR_NOT_FOUND) { err = dc->characteristics ? 0 : status; goto done; } if (status) { err = status; goto done; } list = dec_read_by_type_resp(ipdu, iplen); if (list == NULL) { err = ATT_ECODE_IO; goto done; } if (list->len == 7) type = BT_UUID16; else type = BT_UUID128; for (i = 0; i < list->num; i++) { uint8_t *value = list->data[i]; struct gatt_char *chars; bt_uuid_t uuid128; last = get_le16(value); get_uuid128(type, &value[5], &uuid128); if (dc->uuid && bt_uuid_cmp(dc->uuid, &uuid128)) continue; chars = g_try_new0(struct gatt_char, 1); if (!chars) { att_data_list_free(list); err = ATT_ECODE_INSUFF_RESOURCES; goto done; } chars->handle = last; chars->properties = value[2]; chars->value_handle = get_le16(&value[3]); bt_uuid_to_string(&uuid128, chars->uuid, sizeof(chars->uuid)); dc->characteristics = g_slist_append(dc->characteristics, chars); } att_data_list_free(list); /* * If last handle is lower from previous start handle then it is smth * wrong. Let's stop search, otherwise we might enter infinite loop. */ if (last < dc->start) { err = ATT_ECODE_UNLIKELY; goto done; } dc->start = last + 1; if (last != 0 && (dc->start < dc->end)) { bt_uuid_t uuid; guint16 oplen; size_t buflen; uint8_t *buf; buf = g_attrib_get_buffer(dc->attrib, &buflen); bt_uuid16_create(&uuid, GATT_CHARAC_UUID); oplen = enc_read_by_type_req(dc->start, dc->end, &uuid, buf, buflen); if (oplen == 0) return; g_attrib_send(dc->attrib, dc->id, buf, oplen, char_discovered_cb, discover_char_ref(dc), discover_char_unref); return; } done: dc->cb(err, dc->characteristics, dc->user_data); }
static void desc_discovered_cb(guint8 status, const guint8 *ipdu, guint16 iplen, gpointer user_data) { struct discover_desc *dd = user_data; struct att_data_list *list; unsigned int i, err = 0; guint8 format; uint16_t last = 0xffff; uint8_t type; gboolean uuid_found = FALSE; if (status == ATT_ECODE_ATTR_NOT_FOUND) { err = dd->descriptors ? 0 : status; goto done; } if (status) { err = status; goto done; } list = dec_find_info_resp(ipdu, iplen, &format); if (!list) { err = ATT_ECODE_IO; goto done; } if (format == ATT_FIND_INFO_RESP_FMT_16BIT) type = BT_UUID16; else type = BT_UUID128; for (i = 0; i < list->num; i++) { uint8_t *value = list->data[i]; struct gatt_desc *desc; bt_uuid_t uuid128; last = get_le16(value); get_uuid128(type, &value[2], &uuid128); if (dd->uuid) { if (bt_uuid_cmp(dd->uuid, &uuid128)) continue; else uuid_found = TRUE; } desc = g_try_new0(struct gatt_desc, 1); if (!desc) { att_data_list_free(list); err = ATT_ECODE_INSUFF_RESOURCES; goto done; } bt_uuid_to_string(&uuid128, desc->uuid, sizeof(desc->uuid)); desc->handle = last; if (type == BT_UUID16) desc->uuid16 = get_le16(&value[2]); dd->descriptors = g_slist_append(dd->descriptors, desc); if (uuid_found) break; } att_data_list_free(list); /* * If last handle is lower from previous start handle then it is smth * wrong. Let's stop search, otherwise we might enter infinite loop. */ if (last < dd->start) { err = ATT_ECODE_UNLIKELY; goto done; } dd->start = last + 1; if (last < dd->end && !uuid_found) { guint16 oplen; size_t buflen; uint8_t *buf; buf = g_attrib_get_buffer(dd->attrib, &buflen); oplen = enc_find_info_req(dd->start, dd->end, buf, buflen); if (oplen == 0) return; g_attrib_send(dd->attrib, dd->id, buf, oplen, desc_discovered_cb, discover_desc_ref(dd), discover_desc_unref); return; } done: dd->cb(err, dd->descriptors, dd->user_data); }
static uint8_t read_type_cb(const struct bt_gatt_attr *attr, void *user_data) { struct read_type_data *data = user_data; struct bt_att *att = data->att; struct bt_conn *conn = att->chan.conn; int read; /* Skip if doesn't match */ if (bt_uuid_cmp(attr->uuid, data->uuid)) { return BT_GATT_ITER_CONTINUE; } BT_DBG("handle 0x%04x", attr->handle); /* * If an attribute in the set of requested attributes would cause an * Error Response then this attribute cannot be included in a * Read By Type Response and the attributes before this attribute * shall be returned * * If the first attribute in the set of requested attributes would * cause an Error Response then no other attributes in the requested * attributes can be considered. */ data->err = check_perm(conn, attr, BT_GATT_PERM_READ_MASK); if (data->err) { if (data->rsp->len) { data->err = 0x00; } return BT_GATT_ITER_STOP; } /* * If any attribute is founded in handle range it means that error * should be changed from pre-set: attr not found error to no error. */ data->err = 0x00; /* Fast foward to next item position */ data->item = net_buf_add(data->buf, sizeof(*data->item)); data->item->handle = sys_cpu_to_le16(attr->handle); /* Read attribute value and store in the buffer */ read = attr->read(conn, attr, data->buf->data + data->buf->len, att->chan.tx.mtu - data->buf->len, 0); if (read < 0) { data->err = err_to_att(read); return BT_GATT_ITER_STOP; } if (!data->rsp->len) { /* Set len to be the first item found */ data->rsp->len = read + sizeof(*data->item); } else if (data->rsp->len != read + sizeof(*data->item)) { /* All items should have the same size */ data->buf->len -= sizeof(*data->item); return BT_GATT_ITER_STOP; } net_buf_add(data->buf, read); /* return true only if there are still space for more items */ return att->chan.tx.mtu - data->buf->len > data->rsp->len ? BT_GATT_ITER_CONTINUE : BT_GATT_ITER_STOP; }