static void process_read_by_type(struct async_read_op *op) { struct bt_gatt_server *server = op->server; uint8_t ecode; struct gatt_db_attribute *attr; attr = queue_pop_head(op->db_data); if (op->done || !attr) { bt_att_send(server->att, BT_ATT_OP_READ_BY_TYPE_RSP, op->pdu, op->pdu_len, NULL, NULL, NULL); async_read_op_destroy(op); return; } ecode = check_permissions(server, attr, BT_ATT_PERM_READ | BT_ATT_PERM_READ_AUTHEN | BT_ATT_PERM_READ_ENCRYPT); if (ecode) goto error; if (gatt_db_attribute_read(attr, 0, op->opcode, server->att, read_by_type_read_complete_cb, op)) return; ecode = BT_ATT_ERROR_UNLIKELY; error: bt_att_send_error_rsp(server->att, BT_ATT_OP_READ_BY_TYPE_REQ, gatt_db_attribute_get_handle(attr), ecode); async_read_op_destroy(op); }
static bool encode_read_by_grp_type_rsp(struct gatt_db *db, struct queue *q, struct bt_att *att, uint16_t mtu, uint8_t *pdu, uint16_t *len) { int iter = 0; uint16_t start_handle, end_handle; struct iovec value; uint8_t data_val_len; *len = 0; while (queue_peek_head(q)) { struct gatt_db_attribute *attrib = queue_pop_head(q); value.iov_base = NULL; value.iov_len = 0; /* * This should never be deferred to the read callback for * primary/secondary service declarations. */ if (!gatt_db_attribute_read(attrib, 0, BT_ATT_OP_READ_BY_GRP_TYPE_REQ, att, attribute_read_cb, &value) || !value.iov_len) return false; /* * Use the first attribute to determine the length of each * attribute data unit. Stop the list when a different attribute * value is seen. */ if (iter == 0) { data_val_len = MIN(MIN((unsigned)mtu - 6, 251), value.iov_len); pdu[0] = data_val_len + 4; iter++; } else if (value.iov_len != data_val_len) break; /* Stop if this unit would surpass the MTU */ if (iter + data_val_len + 4 > mtu - 1) break; gatt_db_attribute_get_service_handles(attrib, &start_handle, &end_handle); put_le16(start_handle, pdu + iter); put_le16(end_handle, pdu + iter + 2); memcpy(pdu + iter + 4, value.iov_base, data_val_len); iter += data_val_len + 4; } *len = iter; return true; }
static gboolean characteristic_value_exists(const GDBusPropertyTable *property, void *data) { struct characteristic *chrc = data; gboolean ret; gatt_db_attribute_read(chrc->attr, 0, 0, NULL, read_check_cb, &ret); return TRUE; }
static gboolean descriptor_value_exists(const GDBusPropertyTable *property, void *data) { struct descriptor *desc = data; gboolean ret; gatt_db_attribute_read(desc->attr, 0, 0, NULL, read_check_cb, &ret); return ret; }
static void desc_read_cb(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct async_dbus_op *op = user_data; struct descriptor *desc = op->data; struct service *service = desc->chrc->service; DBusMessage *reply; if (!success) goto fail; if (!op->offset) gatt_db_attribute_reset(desc->attr); if (!gatt_db_attribute_write(desc->attr, op->offset, value, length, 0, NULL, write_descriptor_cb, desc)) { error("Failed to store attribute"); goto fail; } /* * If the value length is exactly MTU-1, then we may not have read the * entire value. Perform a long read to obtain the rest, otherwise, * we're done. */ if (length == bt_gatt_client_get_mtu(service->client->gatt) - 1) { op->offset += length; desc->read_id = bt_gatt_client_read_long_value( service->client->gatt, desc->handle, op->offset, desc_read_cb, async_dbus_op_ref(op), async_dbus_op_unref); if (desc->read_id) return; } /* Read the stored data from db */ if (!gatt_db_attribute_read(desc->attr, 0, 0, NULL, read_op_cb, op)) { error("Failed to read database"); goto fail; } desc->read_id = 0; return; fail: reply = create_gatt_dbus_error(op->msg, att_ecode); desc->read_id = 0; g_dbus_send_message(btd_get_dbus_connection(), reply); return; }
static void handle_read_req(struct bt_gatt_server *server, uint8_t opcode, uint16_t handle, uint16_t offset) { struct gatt_db_attribute *attr; uint8_t ecode; uint32_t perm; struct async_read_op *op = NULL; attr = gatt_db_get_attribute(server->db, handle); if (!attr) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } util_debug(server->debug_callback, server->debug_data, "Read %sReq - handle: 0x%04x", opcode == BT_ATT_OP_READ_BLOB_REQ ? "Blob " : "", handle); perm = gatt_db_attribute_get_permissions(attr); if (perm && !(perm & BT_ATT_PERM_READ)) { ecode = BT_ATT_ERROR_READ_NOT_PERMITTED; goto error; } if (server->pending_read_op) { ecode = BT_ATT_ERROR_UNLIKELY; goto error; } op = new0(struct async_read_op, 1); if (!op) { ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES; goto error; } op->opcode = opcode; op->server = server; server->pending_read_op = op; if (gatt_db_attribute_read(attr, offset, opcode, server->att, read_complete_cb, op)) return; ecode = BT_ATT_ERROR_UNLIKELY; error: if (op) async_read_op_destroy(op); bt_att_send_error_rsp(server->att, opcode, handle, ecode); }
static void read_multiple_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; struct gatt_db_attribute *attr; struct read_multiple_resp_data data; uint8_t ecode = BT_ATT_ERROR_UNLIKELY; size_t i = 0; data.handles = NULL; data.rsp_data = NULL; if (length < 4) { ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } data.server = server; data.num_handles = length / 2; data.cur_handle = 0; data.mtu = bt_att_get_mtu(server->att); data.length = 0; data.rsp_data = malloc(data.mtu - 1); if (!data.rsp_data) goto error; data.handles = new0(uint16_t, data.num_handles); if (!data.handles) goto error; for (i = 0; i < data.num_handles; i++) data.handles[i] = get_le16(pdu + i * 2); util_debug(server->debug_callback, server->debug_data, "Read Multiple Req - %zu handles, 1st: 0x%04x", data.num_handles, data.handles[0]); attr = gatt_db_get_attribute(server->db, data.handles[0]); if (!attr) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } if (gatt_db_attribute_read(attr, 0, opcode, server->att, read_multiple_complete_cb, &data)) return; error: read_multiple_resp_data_free(&data); bt_att_send_error_rsp(server->att, opcode, 0, ecode); }
static void handle_read_req(struct bt_gatt_server *server, uint8_t opcode, uint16_t handle, uint16_t offset) { struct gatt_db_attribute *attr; uint8_t ecode; struct async_read_op *op = NULL; ecode = authorize_req(server, opcode, handle); if (ecode) goto error; attr = gatt_db_get_attribute(server->db, handle); if (!attr) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } util_debug(server->debug_callback, server->debug_data, "Read %sReq - handle: 0x%04x", opcode == BT_ATT_OP_READ_BLOB_REQ ? "Blob " : "", handle); ecode = check_permissions(server, attr, BT_ATT_PERM_READ | BT_ATT_PERM_READ_AUTHEN | BT_ATT_PERM_READ_ENCRYPT); if (ecode) goto error; if (server->pending_read_op) { ecode = BT_ATT_ERROR_UNLIKELY; goto error; } op = new0(struct async_read_op, 1); op->opcode = opcode; op->server = server; server->pending_read_op = op; if (gatt_db_attribute_read(attr, offset, opcode, server->att, read_complete_cb, op)) return; ecode = BT_ATT_ERROR_UNLIKELY; error: if (op) async_read_op_destroy(op); bt_att_send_error_rsp(server->att, opcode, handle, ecode); }
static gboolean characteristic_get_value(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct characteristic *chrc = data; DBusMessageIter array; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array); gatt_db_attribute_read(chrc->attr, 0, 0, NULL, read_cb, &array); dbus_message_iter_close_container(iter, &array); return TRUE; }
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); }
static void chrc_read_cb(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct async_dbus_op *op = user_data; struct characteristic *chrc = op->data; struct service *service = chrc->service; if (!success) { DBusMessage *reply = create_gatt_dbus_error(op->msg, att_ecode); chrc->read_id = 0; g_dbus_send_message(btd_get_dbus_connection(), reply); return ; } if (!op->offset) gatt_db_attribute_reset(chrc->attr); gatt_db_attribute_write(chrc->attr, op->offset, value, length, 0, NULL, write_characteristic_cb, chrc); /* * If the value length is exactly MTU-1, then we may not have read the * entire value. Perform a long read to obtain the rest, otherwise, * we're done. */ if (length == bt_gatt_client_get_mtu(service->client->gatt) - 1) { op->offset += length; chrc->read_id = bt_gatt_client_read_long_value( service->client->gatt, chrc->value_handle, op->offset, chrc_read_cb, async_dbus_op_ref(op), async_dbus_op_unref); if (chrc->read_id) return; } chrc->read_id = 0; /* Read the stored data from db */ gatt_db_attribute_read(chrc->attr, 0, 0, NULL, read_op_cb, op); }
static void process_read_by_type(struct async_read_op *op) { struct bt_gatt_server *server = op->server; uint8_t ecode; struct gatt_db_attribute *attr; uint32_t perm; attr = queue_pop_head(op->db_data); if (op->done || !attr) { bt_att_send(server->att, BT_ATT_OP_READ_BY_TYPE_RSP, op->pdu, op->pdu_len, NULL, NULL, NULL); async_read_op_destroy(op); return; } perm = gatt_db_attribute_get_permissions(attr); /* * Check for the READ access permission. Encryption, * authentication, and authorization permissions need to be * checked by the read handler, since bt_att is agnostic to * connection type and doesn't have security information on it. */ if (perm && !(perm & BT_ATT_PERM_READ)) { ecode = BT_ATT_ERROR_READ_NOT_PERMITTED; goto error; } if (gatt_db_attribute_read(attr, 0, op->opcode, server->att, read_by_type_read_complete_cb, op)) return; ecode = BT_ATT_ERROR_UNLIKELY; error: bt_att_send_error_rsp(server->att, BT_ATT_OP_READ_BY_TYPE_REQ, gatt_db_attribute_get_handle(attr), ecode); async_read_op_destroy(op); }
static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err, const uint8_t *value, size_t len, void *user_data) { struct read_multiple_resp_data *data = user_data; struct gatt_db_attribute *next_attr; uint32_t perm; uint16_t handle = gatt_db_attribute_get_handle(attr); if (err != 0) { bt_att_send_error_rsp(data->server->att, BT_ATT_OP_READ_MULT_REQ, handle, err); read_multiple_resp_data_free(data); return; } perm = gatt_db_attribute_get_permissions(attr); if (perm && !(perm & BT_ATT_PERM_READ)) { bt_att_send_error_rsp(data->server->att, BT_ATT_OP_READ_MULT_REQ, handle, BT_ATT_ERROR_READ_NOT_PERMITTED); read_multiple_resp_data_free(data); return; } len = MIN(len, data->mtu - data->length - 1); memcpy(data->rsp_data + data->length, value, len); data->length += len; data->cur_handle++; if ((data->length >= data->mtu - 1) || (data->cur_handle == data->num_handles)) { bt_att_send(data->server->att, BT_ATT_OP_READ_MULT_RSP, data->rsp_data, data->length, NULL, NULL, NULL); read_multiple_resp_data_free(data); return; } util_debug(data->server->debug_callback, data->server->debug_data, "Read Multiple Req - #%zu of %zu: 0x%04x", data->cur_handle + 1, data->num_handles, data->handles[data->cur_handle]); next_attr = gatt_db_get_attribute(data->server->db, data->handles[data->cur_handle]); if (!next_attr) { bt_att_send_error_rsp(data->server->att, BT_ATT_OP_READ_MULT_REQ, data->handles[data->cur_handle], BT_ATT_ERROR_INVALID_HANDLE); read_multiple_resp_data_free(data); return; } if (!gatt_db_attribute_read(next_attr, 0, BT_ATT_OP_READ_MULT_REQ, data->server->att, read_multiple_complete_cb, data)) { bt_att_send_error_rsp(data->server->att, BT_ATT_OP_READ_MULT_REQ, data->handles[data->cur_handle], BT_ATT_ERROR_UNLIKELY); read_multiple_resp_data_free(data); } }