Return-Path: From: Luiz Augusto von Dentz To: linux-bluetooth@vger.kernel.org Subject: [PATCH BlueZ 2/2] shared/gatt-db: Rework API Date: Wed, 5 Nov 2014 16:11:09 +0200 Message-Id: <1415196669-582-2-git-send-email-luiz.dentz@gmail.com> In-Reply-To: <1415196669-582-1-git-send-email-luiz.dentz@gmail.com> References: <1415196669-582-1-git-send-email-luiz.dentz@gmail.com> Sender: linux-bluetooth-owner@vger.kernel.org List-ID: From: Luiz Augusto von Dentz This rework the API to use gatt_db_attribute whenever possible which simplify the code adn reduces the number of lookups. --- android/gatt.c | 603 +++++++++++++++++++++++++---------------------- src/shared/gatt-db.c | 445 +++++++++++++++------------------- src/shared/gatt-db.h | 87 ++++--- src/shared/gatt-server.c | 37 ++- 4 files changed, 582 insertions(+), 590 deletions(-) diff --git a/android/gatt.c b/android/gatt.c index b3dd6d3..7a7be6d 100644 --- a/android/gatt.c +++ b/android/gatt.c @@ -94,6 +94,8 @@ static const char *device_state_str[] = { struct pending_trans_data { unsigned int id; uint8_t opcode; + struct gatt_db_attribute *attrib; + unsigned int serial_id; }; struct gatt_app { @@ -199,7 +201,7 @@ static struct queue *services_sdp = NULL; static struct queue *listen_apps = NULL; static struct gatt_db *gatt_db = NULL; -static uint16_t service_changed_handle = 0; +static struct gatt_db_attribute *service_changed_attrib = NULL; static GIOChannel *le_io = NULL; static GIOChannel *bredr_io = NULL; @@ -676,7 +678,7 @@ enum pend_req_state { }; struct pending_request { - uint16_t handle; + struct gatt_db_attribute *attrib; int length; uint8_t *value; uint16_t offset; @@ -998,11 +1000,16 @@ static void send_exchange_mtu_request(struct gatt_device *device) static void notify_att_range_change(struct gatt_device *dev, struct att_range *range) { + uint16_t handle; uint16_t length = 0; uint16_t ccc; uint8_t *pdu; size_t mtu; + handle = gatt_db_attribute_get_handle(service_changed_attrib); + if (!handle) + return; + ccc = bt_get_gatt_ccc(&dev->bdaddr); if (!ccc) return; @@ -1011,14 +1018,12 @@ static void notify_att_range_change(struct gatt_device *dev, switch (ccc) { case 0x0001: - length = enc_notification(service_changed_handle, - (uint8_t *) range, + length = enc_notification(handle, (uint8_t *) range, sizeof(*range), pdu, mtu); break; case 0x0002: - length = enc_indication(service_changed_handle, - (uint8_t *) range, sizeof(*range), pdu, - mtu); + length = enc_indication(handle, (uint8_t *) range, + sizeof(*range), pdu, mtu); break; default: /* 0xfff4 reserved for future use */ @@ -1475,7 +1480,7 @@ static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data) * constant all the time, thus they should be excluded from * range indicating changes. */ - range.start = service_changed_handle + 2; + range.start = gatt_db_attribute_get_handle(service_changed_attrib) + 2; range.end = 0xffff; /* @@ -4210,6 +4215,7 @@ static void handle_server_add_service(const void *buf, uint16_t len) const struct hal_cmd_gatt_server_add_service *cmd = buf; struct hal_ev_gatt_server_service_added ev; struct gatt_app *server; + struct gatt_db_attribute *service; uint8_t status; bt_uuid_t uuid; @@ -4225,9 +4231,14 @@ static void handle_server_add_service(const void *buf, uint16_t len) android2uuid(cmd->srvc_id.uuid, &uuid); - ev.srvc_handle = gatt_db_add_service(gatt_db, &uuid, - cmd->srvc_id.is_primary, + service = gatt_db_add_service(gatt_db, &uuid, cmd->srvc_id.is_primary, cmd->num_handles); + if (!service) { + status = HAL_STATUS_FAILED; + goto failed; + } + + ev.srvc_handle = gatt_db_attribute_get_handle(service); if (!ev.srvc_handle) { status = HAL_STATUS_FAILED; goto failed; @@ -4252,6 +4263,7 @@ static void handle_server_add_included_service(const void *buf, uint16_t len) const struct hal_cmd_gatt_server_add_inc_service *cmd = buf; struct hal_ev_gatt_server_inc_srvc_added ev; struct gatt_app *server; + struct gatt_db_attribute *service, *include; uint8_t status; DBG(""); @@ -4264,10 +4276,20 @@ static void handle_server_add_included_service(const void *buf, uint16_t len) goto failed; } - ev.incl_srvc_handle = gatt_db_add_included_service(gatt_db, - cmd->service_handle, - cmd->included_handle); - if (!ev.incl_srvc_handle) { + service = gatt_db_get_attribute(gatt_db, cmd->service_handle); + if (!service) { + status = HAL_STATUS_FAILED; + goto failed; + } + + include = gatt_db_get_attribute(gatt_db, cmd->included_handle); + if (!service) { + status = HAL_STATUS_FAILED; + goto failed; + } + + service = gatt_db_service_add_included(service, include); + if (!service) { status = HAL_STATUS_FAILED; goto failed; } @@ -4360,8 +4382,11 @@ static void send_dev_complete_response(struct gatt_device *device, val = queue_pop_head(temp); while (val) { uint8_t *value = adl->data[iterator++]; + uint16_t handle; - put_le16(val->handle, value); + handle = gatt_db_attribute_get_handle(val->attrib); + + put_le16(handle, value); memcpy(&value[2], val->value, val->length); destroy_pending_request(val); @@ -4426,12 +4451,13 @@ static void send_dev_complete_response(struct gatt_device *device, val = queue_pop_head(temp); while (val) { uint8_t *value = adl->data[iterator++]; - uint16_t end_handle; + uint16_t start_handle, end_handle; - end_handle = gatt_db_get_end_handle(gatt_db, - val->handle); + gatt_db_attribute_get_service_handles(val->attrib, + &start_handle, + &end_handle); - put_le16(val->handle, value); + put_le16(start_handle, value); put_le16(end_handle, &value[2]); memcpy(&value[4], val->value, val->length); @@ -4471,14 +4497,17 @@ static void send_dev_complete_response(struct gatt_device *device, break; } - range->start = val->handle; + range->start = gatt_db_attribute_get_handle( + val->attrib); range->end = range->start; - /* Get proper end handle if its group type */ - type = gatt_db_get_attribute_type(gatt_db, val->handle); + type = gatt_db_attribute_get_type(val->attrib); if (is_service(type)) - range->end = gatt_db_get_end_handle(gatt_db, - val->handle); + range->end = + gatt_db_attribute_get_service_handles( + val->attrib, + NULL, + &range->end); list = g_slist_append(list, range); @@ -4515,17 +4544,22 @@ static void send_dev_complete_response(struct gatt_device *device, len = enc_write_resp(rsp); destroy_pending_request(val); break; - case ATT_OP_PREP_WRITE_REQ: + case ATT_OP_PREP_WRITE_REQ: { + uint16_t handle; + val = queue_pop_head(device->pending_requests); if (val->error) { error = val->error; goto done; } - len = enc_prep_write_resp(val->handle, val->offset, val->value, + handle = gatt_db_attribute_get_handle(val->attrib); + + len = enc_prep_write_resp(handle, val->offset, val->value, val->length, rsp, mtu); destroy_pending_request(val); break; + } default: break; } @@ -4545,12 +4579,11 @@ struct request_processing_data { struct gatt_device *device; }; -static bool match_dev_request_by_handle(const void *data, const void *user_data) +static bool match_dev_request_by_attrib(const void *data, const void *user_data) { const struct pending_request *handle_data = data; - uint16_t handle = PTR_TO_UINT(user_data); - return handle_data->handle == handle; + return handle_data->attrib == user_data; } static uint8_t check_device_permissions(struct gatt_device *device, @@ -4623,11 +4656,12 @@ static uint8_t check_device_permissions(struct gatt_device *device, return 0; } -static void fill_gatt_response(struct pending_request *request, uint16_t handle, +static void fill_gatt_response(struct pending_request *request, + struct gatt_db_attribute *attrib, uint16_t offset, uint8_t status, uint16_t len, const uint8_t *data) { - request->handle = handle; + request->attrib = attrib; request->offset = offset; request->length = len; request->state = REQUEST_DONE; @@ -4646,38 +4680,49 @@ static void fill_gatt_response(struct pending_request *request, uint16_t handle, memcpy(request->value, data, len); } -static void fill_gatt_response_by_handle(uint16_t handle, uint16_t offset, - uint8_t status, uint16_t len, - const uint8_t *data, - struct gatt_device *dev) +static uint8_t err_to_att(int err) { - struct pending_request *entry; + if (!err || (err > 0 && err < UINT8_MAX)) + return err; - entry = queue_find(dev->pending_requests, match_dev_request_by_handle, - UINT_TO_PTR(handle)); - if (!entry) { - error("gatt: No pending response! Bogus android response?"); - return; + switch (err) { + case -ENOENT: + return ATT_ECODE_INVALID_HANDLE; + case -ENOMEM: + return ATT_ECODE_INSUFF_RESOURCES; + default: + return ATT_ECODE_UNLIKELY; } +} + +static void attribute_read_cb(struct gatt_db_attribute *attrib, int err, + const uint8_t *value, size_t length, + void *user_data) +{ + struct pending_request *resp_data = user_data; + uint8_t error = err_to_att(err); - fill_gatt_response(entry, handle, offset, status, len, data); + fill_gatt_response(resp_data, attrib, resp_data->offset, error, length, + value); } static void read_requested_attributes(void *data, void *user_data) { struct pending_request *resp_data = data; struct request_processing_data *process_data = user_data; + struct gatt_db_attribute *attrib; uint32_t permissions; - uint8_t *value = NULL, error; - int value_len = 0; + uint8_t error; - if (!gatt_db_get_attribute_permissions(gatt_db, resp_data->handle, - &permissions)) { + attrib = resp_data->attrib; + if (!attrib) { resp_data->error = ATT_ECODE_ATTR_NOT_FOUND; resp_data->state = REQUEST_DONE; return; } + gatt_db_attribute_get_permissions(attrib, &permissions); + /* * Check if it is attribute we didn't declare permissions, like service * declaration or included service. Set permissions to read only @@ -4696,18 +4741,9 @@ static void read_requested_attributes(void *data, void *user_data) resp_data->state = REQUEST_PENDING; - if (!gatt_db_read(gatt_db, resp_data->handle, - resp_data->offset, - process_data->opcode, - &process_data->device->bdaddr, - &value, &value_len)) - error = ATT_ECODE_UNLIKELY; - - /* We have value here already if no callback will be called */ - if (value_len >= 0) - fill_gatt_response(resp_data, resp_data->handle, - resp_data->offset, error, value_len, - value); + gatt_db_attribute_read(attrib, resp_data->offset, process_data->opcode, + &process_data->device->bdaddr, + attribute_read_cb, resp_data); } static void process_dev_pending_requests(struct gatt_device *device, @@ -4729,7 +4765,9 @@ static void process_dev_pending_requests(struct gatt_device *device, } static struct pending_trans_data *conn_add_transact(struct app_connection *conn, - uint8_t opcode) + uint8_t opcode, + struct gatt_db_attribute *attrib, + unsigned int serial_id) { struct pending_trans_data *transaction; static int32_t trans_id = 1; @@ -4745,21 +4783,25 @@ static struct pending_trans_data *conn_add_transact(struct app_connection *conn, transaction->id = trans_id++; transaction->opcode = opcode; + transaction->attrib = attrib; + transaction->serial_id = serial_id; return transaction; } -static void read_cb(uint16_t handle, uint16_t offset, uint8_t att_opcode, - bdaddr_t *bdaddr, void *user_data) +static void read_cb(struct gatt_db_attribute *attrib, unsigned int id, + uint16_t offset, uint8_t opcode, bdaddr_t *bdaddr, + void *user_data) { struct pending_trans_data *transaction; struct hal_ev_gatt_server_request_read ev; struct gatt_app *app; struct app_connection *conn; - int32_t id = PTR_TO_INT(user_data); - struct gatt_device *dev; + int32_t app_id = PTR_TO_INT(user_data); - app = find_app_by_id(id); + DBG("id %u", id); + + app = find_app_by_id(app_id); if (!app) { error("gatt: read_cb, cound not found app id"); goto failed; @@ -4774,15 +4816,15 @@ static void read_cb(uint16_t handle, uint16_t offset, uint8_t att_opcode, memset(&ev, 0, sizeof(ev)); /* Store the request data, complete callback and transaction id */ - transaction = conn_add_transact(conn, att_opcode); + transaction = conn_add_transact(conn, opcode, attrib, id); if (!transaction) goto failed; bdaddr2android(bdaddr, ev.bdaddr); ev.conn_id = conn->id; - ev.attr_handle = handle; + ev.attr_handle = gatt_db_attribute_get_handle(attrib); ev.offset = offset; - ev.is_long = att_opcode == ATT_OP_READ_BLOB_REQ; + ev.is_long = opcode == ATT_OP_READ_BLOB_REQ; ev.trans_id = transaction->id; ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, @@ -4792,26 +4834,23 @@ static void read_cb(uint16_t handle, uint16_t offset, uint8_t att_opcode, return; failed: - dev = find_device_by_addr(bdaddr); - if (dev) - fill_gatt_response_by_handle(handle, 0, ATT_ECODE_UNLIKELY, 0, - NULL, dev); + gatt_db_attribute_read_result(attrib, id, -ENOENT, NULL, 0); } -static void write_cb(uint16_t handle, uint16_t offset, - const uint8_t *value, size_t len, - uint8_t att_opcode, bdaddr_t *bdaddr, - void *user_data) +static void write_cb(struct gatt_db_attribute *attrib, unsigned int id, + uint16_t offset, const uint8_t *value, size_t len, + uint8_t opcode, bdaddr_t *bdaddr, void *user_data) { uint8_t buf[IPC_MTU]; struct hal_ev_gatt_server_request_write *ev = (void *) buf; struct pending_trans_data *transaction; struct gatt_app *app; - int32_t id = PTR_TO_INT(user_data); + int32_t app_id = PTR_TO_INT(user_data); struct app_connection *conn; - struct gatt_device *dev; - app = find_app_by_id(id); + DBG("id %u", id); + + app = find_app_by_id(app_id); if (!app) { error("gatt: write_cb could not found app id"); goto failed; @@ -4827,27 +4866,26 @@ static void write_cb(uint16_t handle, uint16_t offset, * Remember that this application has ongoing prep write * Need it later to find out where to send execute write */ - if (att_opcode == ATT_OP_PREP_WRITE_REQ) + if (opcode == ATT_OP_PREP_WRITE_REQ) conn->wait_execute_write = true; /* Store the request data, complete callback and transaction id */ - transaction = conn_add_transact(conn, att_opcode); + transaction = conn_add_transact(conn, opcode, attrib, id); if (!transaction) goto failed; memset(ev, 0, sizeof(*ev)); bdaddr2android(bdaddr, &ev->bdaddr); - ev->attr_handle = handle; + ev->attr_handle = gatt_db_attribute_get_handle(attrib); ev->offset = offset; ev->conn_id = conn->id; ev->trans_id = transaction->id; - ev->is_prep = att_opcode == ATT_OP_PREP_WRITE_REQ; + ev->is_prep = opcode == ATT_OP_PREP_WRITE_REQ; - if (att_opcode == ATT_OP_WRITE_REQ || - att_opcode == ATT_OP_PREP_WRITE_REQ) + if (opcode == ATT_OP_WRITE_REQ || opcode == ATT_OP_PREP_WRITE_REQ) ev->need_rsp = 0x01; ev->length = len; @@ -4859,10 +4897,7 @@ static void write_cb(uint16_t handle, uint16_t offset, return; failed: - dev = find_device_by_addr(bdaddr); - if (dev) - fill_gatt_response_by_handle(handle, 0, ATT_ECODE_UNLIKELY, 0, - NULL, dev); + gatt_db_attribute_write_result(attrib, id, ATT_ECODE_UNLIKELY); } static uint32_t android_to_gatt_permissions(int32_t hal_permissions) @@ -4904,6 +4939,7 @@ static void handle_server_add_characteristic(const void *buf, uint16_t len) const struct hal_cmd_gatt_server_add_characteristic *cmd = buf; struct hal_ev_gatt_server_characteristic_added ev; struct gatt_app *server; + struct gatt_db_attribute *attrib; bt_uuid_t uuid; uint8_t status; uint32_t permissions; @@ -4919,22 +4955,28 @@ static void handle_server_add_characteristic(const void *buf, uint16_t len) goto failed; } + attrib = gatt_db_get_attribute(gatt_db, cmd->service_handle); + if (!attrib) { + status = HAL_STATUS_FAILED; + goto failed; + } + android2uuid(cmd->uuid, &uuid); permissions = android_to_gatt_permissions(cmd->permissions); - ev.char_handle = gatt_db_add_characteristic(gatt_db, - cmd->service_handle, + attrib = gatt_db_service_add_characteristic(attrib, &uuid, permissions, cmd->properties, read_cb, write_cb, INT_TO_PTR(app_id)); - if (!ev.char_handle) + if (!attrib) status = HAL_STATUS_FAILED; else status = HAL_STATUS_SUCCESS; failed: ev.srvc_handle = cmd->service_handle; + ev.char_handle = gatt_db_attribute_get_handle(attrib); ev.status = status; ev.server_if = app_id; ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE; @@ -4952,6 +4994,7 @@ static void handle_server_add_descriptor(const void *buf, uint16_t len) const struct hal_cmd_gatt_server_add_descriptor *cmd = buf; struct hal_ev_gatt_server_descriptor_added ev; struct gatt_app *server; + struct gatt_db_attribute *attrib; bt_uuid_t uuid; uint8_t status; uint32_t permissions; @@ -4970,12 +5013,16 @@ static void handle_server_add_descriptor(const void *buf, uint16_t len) android2uuid(cmd->uuid, &uuid); permissions = android_to_gatt_permissions(cmd->permissions); - ev.descr_handle = gatt_db_add_char_descriptor(gatt_db, - cmd->service_handle, - &uuid, permissions, + attrib = gatt_db_get_attribute(gatt_db, cmd->service_handle); + if (!attrib) { + status = HAL_STATUS_FAILED; + goto failed; + } + + attrib = gatt_db_service_add_descriptor(attrib, &uuid, permissions, read_cb, write_cb, INT_TO_PTR(app_id)); - if (!ev.descr_handle) + if (!attrib) status = HAL_STATUS_FAILED; else status = HAL_STATUS_SUCCESS; @@ -4996,9 +5043,9 @@ failed: static void notify_service_change(void *data, void *user_data) { struct att_range range; + struct gatt_db_attribute *attrib = user_data; - range.start = PTR_TO_UINT(user_data); - range.end = gatt_db_get_end_handle(gatt_db, range.start); + gatt_db_attribute_get_service_handles(attrib, &range.start, &range.end); /* In case of db error */ if (!range.end) @@ -5105,13 +5152,18 @@ static struct service_sdp *new_service_sdp_record(int32_t service_handle) { bt_uuid_t uuid; struct service_sdp *s; + struct gatt_db_attribute *attrib; uint16_t end_handle; - end_handle = gatt_db_get_end_handle(gatt_db, service_handle); + attrib = gatt_db_get_attribute(gatt_db, service_handle); + if (!attrib) + return NULL; + + gatt_db_attribute_get_service_handles(attrib, NULL, &end_handle); if (!end_handle) return NULL; - if (!gatt_db_get_service_uuid(gatt_db, service_handle, &uuid)) + if (!gatt_db_attribute_get_service_uuid(attrib, &uuid)) return NULL; s = new0(struct service_sdp, 1); @@ -5177,6 +5229,7 @@ static void handle_server_start_service(const void *buf, uint16_t len) const struct hal_cmd_gatt_server_start_service *cmd = buf; struct hal_ev_gatt_server_service_started ev; struct gatt_app *server; + struct gatt_db_attribute *attrib; uint8_t status; DBG(""); @@ -5204,7 +5257,13 @@ static void handle_server_start_service(const void *buf, uint16_t len) goto failed; } - if (!gatt_db_service_set_active(gatt_db, cmd->service_handle, true)) { + attrib = gatt_db_get_attribute(gatt_db, cmd->service_handle); + if (!attrib) { + status = HAL_STATUS_FAILED; + goto failed; + } + + if (!gatt_db_service_set_active(attrib, true)) { /* * no need to clean SDP since this can fail only if service * handle is invalid in which case add_sdp_record() also fails @@ -5213,8 +5272,7 @@ static void handle_server_start_service(const void *buf, uint16_t len) goto failed; } - queue_foreach(gatt_devices, notify_service_change, - UINT_TO_PTR(cmd->service_handle)); + queue_foreach(gatt_devices, notify_service_change, attrib); status = HAL_STATUS_SUCCESS; @@ -5235,6 +5293,7 @@ static void handle_server_stop_service(const void *buf, uint16_t len) const struct hal_cmd_gatt_server_stop_service *cmd = buf; struct hal_ev_gatt_server_service_stopped ev; struct gatt_app *server; + struct gatt_db_attribute *attrib; uint8_t status; DBG(""); @@ -5247,7 +5306,13 @@ static void handle_server_stop_service(const void *buf, uint16_t len) goto failed; } - if (!gatt_db_service_set_active(gatt_db, cmd->service_handle, false)) { + attrib = gatt_db_get_attribute(gatt_db, cmd->service_handle); + if (!attrib) { + status = HAL_STATUS_FAILED; + goto failed; + } + + if (!gatt_db_service_set_active(attrib, false)) { status = HAL_STATUS_FAILED; goto failed; } @@ -5256,8 +5321,7 @@ static void handle_server_stop_service(const void *buf, uint16_t len) status = HAL_STATUS_SUCCESS; - queue_foreach(gatt_devices, notify_service_change, - UINT_TO_PTR(cmd->service_handle)); + queue_foreach(gatt_devices, notify_service_change, attrib); failed: ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE; @@ -5276,6 +5340,7 @@ static void handle_server_delete_service(const void *buf, uint16_t len) const struct hal_cmd_gatt_server_delete_service *cmd = buf; struct hal_ev_gatt_server_service_deleted ev; struct gatt_app *server; + struct gatt_db_attribute *attrib; uint8_t status; DBG(""); @@ -5288,7 +5353,13 @@ static void handle_server_delete_service(const void *buf, uint16_t len) goto failed; } - if (!gatt_db_remove_service(gatt_db, cmd->service_handle)) { + attrib = gatt_db_get_attribute(gatt_db, cmd->service_handle); + if (!attrib) { + status = HAL_STATUS_FAILED; + goto failed; + } + + if (!gatt_db_remove_service(gatt_db, attrib)) { status = HAL_STATUS_FAILED; goto failed; } @@ -5377,7 +5448,6 @@ static void handle_server_send_response(const void *buf, uint16_t len) { const struct hal_cmd_gatt_server_send_response *cmd = buf; struct pending_trans_data *transaction; - uint16_t handle = cmd->handle; struct app_connection *conn; uint8_t status; @@ -5405,17 +5475,22 @@ static void handle_server_send_response(const void *buf, uint16_t len) if (pending_execute_write()) goto done; - /* Make sure handle is 0. We need it to find pending request */ - handle = 0; - /* * FIXME: Handle situation when not all server applications * respond with a success. */ } - fill_gatt_response_by_handle(handle, cmd->offset, cmd->status, cmd->len, - cmd->data, conn->device); + if (transaction->opcode < ATT_OP_WRITE_REQ) + gatt_db_attribute_read_result(transaction->attrib, + transaction->serial_id, + cmd->status, + cmd->data, cmd->len); + else + gatt_db_attribute_write_result(transaction->attrib, + transaction->serial_id, + cmd->status); + send_dev_complete_response(conn->device, transaction->opcode); done: @@ -5564,7 +5639,7 @@ static uint8_t read_by_group_type(const uint8_t *cmd, uint16_t cmd_len, } while (queue_peek_head(q)) { - uint16_t handle = PTR_TO_UINT(queue_pop_head(q)); + struct gatt_db_attribute *attrib = queue_pop_head(q); struct pending_request *entry; entry = new0(struct pending_request, 1); @@ -5573,7 +5648,7 @@ static uint8_t read_by_group_type(const uint8_t *cmd, uint16_t cmd_len, return ATT_ECODE_UNLIKELY; } - entry->handle = handle; + entry->attrib = attrib; entry->state = REQUEST_INIT; if (!queue_push_tail(device->pending_requests, entry)) { @@ -5621,7 +5696,7 @@ static uint8_t read_by_type(const uint8_t *cmd, uint16_t cmd_len, while (queue_peek_head(q)) { struct pending_request *data; - uint16_t handle = PTR_TO_UINT(queue_pop_head(q)); + struct gatt_db_attribute *attrib = queue_pop_head(q); data = new0(struct pending_request, 1); if (!data) { @@ -5630,7 +5705,7 @@ static uint8_t read_by_type(const uint8_t *cmd, uint16_t cmd_len, } data->state = REQUEST_INIT; - data->handle = handle; + data->attrib = attrib; queue_push_tail(device->pending_requests, data); } @@ -5644,6 +5719,7 @@ static uint8_t read_by_type(const uint8_t *cmd, uint16_t cmd_len, static uint8_t read_request(const uint8_t *cmd, uint16_t cmd_len, struct gatt_device *dev) { + struct gatt_db_attribute *attrib; uint16_t handle; uint16_t len; uint16_t offset; @@ -5668,7 +5744,8 @@ static uint8_t read_request(const uint8_t *cmd, uint16_t cmd_len, return ATT_ECODE_REQ_NOT_SUPP; } - if (handle == 0) + attrib = gatt_db_get_attribute(gatt_db, handle); + if (attrib == 0) return ATT_ECODE_INVALID_HANDLE; data = new0(struct pending_request, 1); @@ -5676,7 +5753,7 @@ static uint8_t read_request(const uint8_t *cmd, uint16_t cmd_len, return ATT_ECODE_INSUFF_RESOURCES; data->offset = offset; - data->handle = handle; + data->attrib = attrib; data->state = REQUEST_INIT; if (!queue_push_tail(dev->pending_requests, data)) { free(data); @@ -5775,14 +5852,16 @@ static uint8_t find_info_handle(const uint8_t *cmd, uint16_t cmd_len, while (queue_peek_head(q)) { uint8_t *value; const bt_uuid_t *type; - uint16_t handle = PTR_TO_UINT(queue_pop_head(q)); + struct gatt_db_attribute *attrib = queue_pop_head(q); + uint16_t handle; - type = gatt_db_get_attribute_type(gatt_db, handle); + type = gatt_db_attribute_get_type(attrib); if (!type) break; value = adl->data[iterator++]; + handle = gatt_db_attribute_get_handle(attrib); put_le16(handle, value); memcpy(&value[2], &type->value.u16, bt_uuid_len(type)); } @@ -5805,7 +5884,6 @@ static uint8_t find_by_type_request(const uint8_t *cmd, uint16_t cmd_len, uint8_t search_value[cmd_len]; size_t search_vlen; uint16_t start, end; - uint16_t handle; struct queue *q; bt_uuid_t uuid; uint16_t len; @@ -5826,8 +5904,8 @@ static uint8_t find_by_type_request(const uint8_t *cmd, uint16_t cmd_len, gatt_db_find_by_type(gatt_db, start, end, &uuid, q); - handle = PTR_TO_UINT(queue_pop_head(q)); - while (handle) { + while (queue_peek_head(q)) { + struct gatt_db_attribute *attrib = queue_pop_head(q); struct pending_request *data; data = new0(struct pending_request, 1); @@ -5844,13 +5922,11 @@ static uint8_t find_by_type_request(const uint8_t *cmd, uint16_t cmd_len, } data->state = REQUEST_INIT; - data->handle = handle; + data->attrib = attrib; data->filter_vlen = search_vlen; memcpy(data->filter_value, search_value, search_vlen); queue_push_tail(device->pending_requests, data); - - handle = PTR_TO_UINT(queue_pop_head(q)); } queue_destroy(q, NULL); @@ -5864,6 +5940,7 @@ static void write_cmd_request(const uint8_t *cmd, uint16_t cmd_len, struct gatt_device *dev) { uint8_t value[cmd_len]; + struct gatt_db_attribute *attrib; uint32_t permissions; uint16_t handle; uint16_t len; @@ -5876,13 +5953,18 @@ static void write_cmd_request(const uint8_t *cmd, uint16_t cmd_len, if (handle == 0) return; - if (!gatt_db_get_attribute_permissions(gatt_db, handle, &permissions)) + attrib = gatt_db_get_attribute(gatt_db, handle); + if (!attrib) + return; + + if (!gatt_db_attribute_get_permissions(attrib, &permissions)) return; if (check_device_permissions(dev, cmd[0], permissions)) return; - gatt_db_write(gatt_db, handle, 0, value, vlen, cmd[0], &dev->bdaddr); + gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0], &dev->bdaddr, + NULL, NULL); } static void write_signed_cmd_request(const uint8_t *cmd, uint16_t cmd_len, @@ -5890,6 +5972,7 @@ static void write_signed_cmd_request(const uint8_t *cmd, uint16_t cmd_len, { uint8_t value[ATT_DEFAULT_LE_MTU]; uint8_t s[ATT_SIGNATURE_LEN]; + struct gatt_db_attribute *attrib; uint32_t permissions; uint16_t handle; uint16_t len; @@ -5919,9 +6002,12 @@ static void write_signed_cmd_request(const uint8_t *cmd, uint16_t cmd_len, if (handle == 0) return; - if (!gatt_db_get_attribute_permissions(gatt_db, handle, &permissions)) + attrib = gatt_db_get_attribute(gatt_db, handle); + if (!attrib) return; + gatt_db_attribute_get_permissions(attrib, &permissions); + if (check_device_permissions(dev, cmd[0], permissions)) return; @@ -5949,16 +6035,28 @@ static void write_signed_cmd_request(const uint8_t *cmd, uint16_t cmd_len, } /* Signature OK, proceed with write */ bt_update_sign_counter(&dev->bdaddr, REMOTE_CSRK, r_sign_cnt); - gatt_db_write(gatt_db, handle, 0, value, vlen, cmd[0], - &dev->bdaddr); + gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0], + &dev->bdaddr, NULL, NULL); } } +static void attribute_write_cb(struct gatt_db_attribute *attrib, int err, + void *user_data) +{ + struct pending_request *data = user_data; + uint8_t error = err_to_att(err); + + DBG(""); + + fill_gatt_response(data, attrib, data->offset, error, 0, NULL); +} + static uint8_t write_req_request(const uint8_t *cmd, uint16_t cmd_len, struct gatt_device *dev) { uint8_t value[cmd_len]; struct pending_request *data; + struct gatt_db_attribute *attrib; uint32_t permissions; uint16_t handle; uint16_t len; @@ -5972,9 +6070,12 @@ static uint8_t write_req_request(const uint8_t *cmd, uint16_t cmd_len, if (handle == 0) return ATT_ECODE_INVALID_HANDLE; - if (!gatt_db_get_attribute_permissions(gatt_db, handle, &permissions)) + attrib = gatt_db_get_attribute(gatt_db, handle); + if (!attrib) return ATT_ECODE_ATTR_NOT_FOUND; + gatt_db_attribute_get_permissions(attrib, &permissions); + error = check_device_permissions(dev, cmd[0], permissions); if (error) return error; @@ -5983,7 +6084,7 @@ static uint8_t write_req_request(const uint8_t *cmd, uint16_t cmd_len, if (!data) return ATT_ECODE_INSUFF_RESOURCES; - data->handle = handle; + data->attrib = attrib; data->state = REQUEST_PENDING; if (!queue_push_tail(dev->pending_requests, data)) { @@ -5991,8 +6092,9 @@ static uint8_t write_req_request(const uint8_t *cmd, uint16_t cmd_len, return ATT_ECODE_INSUFF_RESOURCES; } - if (!gatt_db_write(gatt_db, handle, 0, value, vlen, cmd[0], - &dev->bdaddr)) { + if (!gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0], + &dev->bdaddr, attribute_write_cb, + data)) { queue_remove(dev->pending_requests, data); free(data); return ATT_ECODE_UNLIKELY; @@ -6008,6 +6110,7 @@ static uint8_t write_prep_request(const uint8_t *cmd, uint16_t cmd_len, { uint8_t value[cmd_len]; struct pending_request *data; + struct gatt_db_attribute *attrib; uint32_t permissions; uint16_t handle; uint16_t offset; @@ -6023,9 +6126,12 @@ static uint8_t write_prep_request(const uint8_t *cmd, uint16_t cmd_len, if (handle == 0) return ATT_ECODE_INVALID_HANDLE; - if (!gatt_db_get_attribute_permissions(gatt_db, handle, &permissions)) + attrib = gatt_db_get_attribute(gatt_db, handle); + if (!attrib) return ATT_ECODE_ATTR_NOT_FOUND; + gatt_db_attribute_get_permissions(attrib, &permissions); + error = check_device_permissions(dev, cmd[0], permissions); if (error) return error; @@ -6034,7 +6140,7 @@ static uint8_t write_prep_request(const uint8_t *cmd, uint16_t cmd_len, if (!data) return ATT_ECODE_INSUFF_RESOURCES; - data->handle = handle; + data->attrib = attrib; data->offset = offset; data->state = REQUEST_PENDING; @@ -6043,8 +6149,8 @@ static uint8_t write_prep_request(const uint8_t *cmd, uint16_t cmd_len, return ATT_ECODE_INSUFF_RESOURCES; } - if (!gatt_db_write(gatt_db, handle, offset, value, vlen, cmd[0], - &dev->bdaddr)) + if (!gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0], + &dev->bdaddr, attribute_write_cb, data)) return ATT_ECODE_UNLIKELY; return 0; @@ -6061,7 +6167,7 @@ static void send_server_write_execute_notify(void *data, void *user_data) ev->conn_id = conn->id; - transaction = conn_add_transact(conn, ATT_OP_EXEC_WRITE_REQ); + transaction = conn_add_transact(conn, ATT_OP_EXEC_WRITE_REQ, NULL, 0); if (!transaction) { conn->wait_execute_write = false; return; @@ -6258,12 +6364,12 @@ drop: } struct gap_srvc_handles { - uint16_t srvc; + struct gatt_db_attribute *srvc; /* Characteristics */ - uint16_t dev_name; - uint16_t appear; - uint16_t priv; + struct gatt_db_attribute *dev_name; + struct gatt_db_attribute *appear; + struct gatt_db_attribute *priv; }; static struct gap_srvc_handles gap_srvc_data; @@ -6271,8 +6377,9 @@ static struct gap_srvc_handles gap_srvc_data; #define APPEARANCE_GENERIC_PHONE 0x0040 #define PERIPHERAL_PRIVACY_DISABLE 0x00 -static void gap_read_cb(uint16_t handle, uint16_t offset, uint8_t att_opcode, - bdaddr_t *bdaddr, void *user_data) +static void gap_read_cb(struct gatt_db_attribute *attrib, unsigned int id, + uint16_t offset, uint8_t opcode, bdaddr_t *bdaddr, + void *user_data) { struct pending_request *entry; struct gatt_device *dev; @@ -6285,12 +6392,12 @@ static void gap_read_cb(uint16_t handle, uint16_t offset, uint8_t att_opcode, return; } - entry = queue_find(dev->pending_requests, match_dev_request_by_handle, - UINT_TO_PTR(handle)); + entry = queue_find(dev->pending_requests, match_dev_request_by_attrib, + attrib); if (!entry) return; - if (handle == gap_srvc_data.dev_name) { + if (attrib == gap_srvc_data.dev_name) { const char *name = bt_get_adapter_name(); entry->value = malloc0(strlen(name)); @@ -6301,7 +6408,7 @@ static void gap_read_cb(uint16_t handle, uint16_t offset, uint8_t att_opcode, entry->length = strlen(name); memcpy(entry->value, bt_get_adapter_name(), entry->length); - } else if (handle == gap_srvc_data.appear) { + } else if (attrib == gap_srvc_data.appear) { entry->value = malloc0(2); if (!entry->value) { entry->error = ATT_ECODE_INSUFF_RESOURCES; @@ -6310,7 +6417,7 @@ static void gap_read_cb(uint16_t handle, uint16_t offset, uint8_t att_opcode, put_le16(APPEARANCE_GENERIC_PHONE, entry->value); entry->length = sizeof(uint8_t) * 2; - } else if (handle == gap_srvc_data.priv) { + } else if (attrib == gap_srvc_data.priv) { entry->value = malloc0(1); if (!entry->value) { entry->error = ATT_ECODE_INSUFF_RESOURCES; @@ -6341,7 +6448,7 @@ static void register_gap_service(void) /* Device name characteristic */ bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME); gap_srvc_data.dev_name = - gatt_db_add_characteristic(gatt_db, gap_srvc_data.srvc, + gatt_db_service_add_characteristic(gap_srvc_data.srvc, &uuid, GATT_PERM_READ, GATT_CHR_PROP_READ, gap_read_cb, NULL, @@ -6350,7 +6457,7 @@ static void register_gap_service(void) /* Appearance */ bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE); gap_srvc_data.appear = - gatt_db_add_characteristic(gatt_db, gap_srvc_data.srvc, + gatt_db_service_add_characteristic(gap_srvc_data.srvc, &uuid, GATT_PERM_READ, GATT_CHR_PROP_READ, gap_read_cb, NULL, @@ -6359,29 +6466,28 @@ static void register_gap_service(void) /* Pripheral privacy flag */ bt_uuid16_create(&uuid, GATT_CHARAC_PERIPHERAL_PRIV_FLAG); gap_srvc_data.priv = - gatt_db_add_characteristic(gatt_db, gap_srvc_data.srvc, + gatt_db_service_add_characteristic(gap_srvc_data.srvc, &uuid, GATT_PERM_READ, GATT_CHR_PROP_READ, gap_read_cb, NULL, NULL); - gatt_db_service_set_active(gatt_db, gap_srvc_data.srvc , true); + gatt_db_service_set_active(gap_srvc_data.srvc , true); /* SDP */ bt_uuid16_create(&uuid, 0x1800); - start = gap_srvc_data.srvc; - end = gatt_db_get_end_handle(gatt_db, gap_srvc_data.srvc); + gatt_db_attribute_get_service_handles(gap_srvc_data.srvc, &start, &end); gap_sdp_handle = add_sdp_record(&uuid, start, end, "Generic Access Profile"); if (!gap_sdp_handle) error("gatt: Failed to register GAP SDP record"); } -static void device_info_read_cb(uint16_t handle, uint16_t offset, - uint8_t att_opcode, bdaddr_t *bdaddr, +static void device_info_read_cb(struct gatt_db_attribute *attrib, + unsigned int id, uint16_t offset, + uint8_t opcode, bdaddr_t *bdaddr, void *user_data) { - struct pending_request *entry; struct gatt_device *dev; char *buf = user_data; @@ -6391,31 +6497,16 @@ static void device_info_read_cb(uint16_t handle, uint16_t offset, return; } - entry = queue_find(dev->pending_requests, match_dev_request_by_handle, - UINT_TO_PTR(handle)); - if (!entry) - return; - - entry->value = malloc0(strlen(buf)); - if (!entry->value) { - entry->error = ATT_ECODE_UNLIKELY; - goto done; - } - - entry->length = strlen(buf); - memcpy(entry->value, buf, entry->length); - entry->offset = offset; - -done: - entry->state = REQUEST_DONE; + gatt_db_attribute_read_result(attrib, id, 0, user_data, strlen(buf)); } -static void device_info_read_system_id_cb(uint16_t handle, uint16_t offset, - uint8_t att_opcode, bdaddr_t *bdaddr, +static void device_info_read_system_id_cb(struct gatt_db_attribute *attrib, + unsigned int id, uint16_t offset, + uint8_t opcode, bdaddr_t *bdaddr, void *user_data) { - struct pending_request *entry; struct gatt_device *dev; + uint8_t pdu[8]; dev = find_device_by_addr(bdaddr); if (!dev) { @@ -6423,31 +6514,18 @@ static void device_info_read_system_id_cb(uint16_t handle, uint16_t offset, return; } - entry = queue_find(dev->pending_requests, match_dev_request_by_handle, - UINT_TO_PTR(handle)); - if (!entry) - return; - - entry->value = malloc0(sizeof(uint64_t)); - if (!entry->value) { - entry->error = ATT_ECODE_UNLIKELY; - goto done; - } - - entry->length = sizeof(uint64_t); - put_le64(bt_config_get_system_id(), entry->value); - entry->offset = offset; + put_le64(bt_config_get_system_id(), pdu); -done: - entry->state = REQUEST_DONE; + gatt_db_attribute_read_result(attrib, id, 0, pdu, sizeof(pdu)); } -static void device_info_read_pnp_id_cb(uint16_t handle, uint16_t offset, - uint8_t att_opcode, bdaddr_t *bdaddr, +static void device_info_read_pnp_id_cb(struct gatt_db_attribute *attrib, + unsigned int id, uint16_t offset, + uint8_t opcode, bdaddr_t *bdaddr, void *user_data) { - struct pending_request *entry; struct gatt_device *dev; + uint8_t pdu[7]; dev = find_device_by_addr(bdaddr); if (!dev) { @@ -6455,34 +6533,19 @@ static void device_info_read_pnp_id_cb(uint16_t handle, uint16_t offset, return; } - entry = queue_find(dev->pending_requests, match_dev_request_by_handle, - UINT_TO_PTR(handle)); - if (!entry) - return; - - entry->value = malloc0(sizeof(uint8_t) + 3 * sizeof(uint16_t)); - if (!entry->value) { - entry->error = ATT_ECODE_UNLIKELY; - goto done; - } - - entry->length = sizeof(uint8_t) + 3 * sizeof(uint16_t); - - entry->value[0] = bt_config_get_pnp_source(); - put_le16(bt_config_get_pnp_vendor(), entry->value + 1); - put_le16(bt_config_get_pnp_product(), entry->value + 3); - put_le16(bt_config_get_pnp_version(), entry->value + 5); - - entry->offset = offset; + pdu[0] = bt_config_get_pnp_source(); + put_le16(bt_config_get_pnp_vendor(), &pdu[1]); + put_le16(bt_config_get_pnp_product(), &pdu[3]); + put_le16(bt_config_get_pnp_version(), &pdu[5]); -done: - entry->state = REQUEST_DONE; + gatt_db_attribute_read_result(attrib, id, 0, pdu, sizeof(pdu)); } static void register_device_info_service(void) { bt_uuid_t uuid; - uint16_t srvc_handle, end_handle; + struct gatt_db_attribute *service; + uint16_t start_handle, end_handle; const char *data; uint32_t enc_perm = GATT_PERM_READ | GATT_PERM_READ_ENCRYPTED; @@ -6490,13 +6553,13 @@ static void register_device_info_service(void) /* Device Information Service */ bt_uuid16_create(&uuid, 0x180a); - srvc_handle = gatt_db_add_service(gatt_db, &uuid, true, 15); + service = gatt_db_add_service(gatt_db, &uuid, true, 15); /* User data are not const hence (void *) cast is used */ data = bt_config_get_name(); if (data) { bt_uuid16_create(&uuid, GATT_CHARAC_MODEL_NUMBER_STRING); - gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, + gatt_db_service_add_characteristic(service, &uuid, GATT_PERM_READ, GATT_CHR_PROP_READ, device_info_read_cb, NULL, @@ -6506,7 +6569,7 @@ static void register_device_info_service(void) data = bt_config_get_serial(); if (data) { bt_uuid16_create(&uuid, GATT_CHARAC_SERIAL_NUMBER_STRING); - gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, + gatt_db_service_add_characteristic(service, &uuid, enc_perm, GATT_CHR_PROP_READ, device_info_read_cb, NULL, (void *) data); @@ -6514,7 +6577,7 @@ static void register_device_info_service(void) if (bt_config_get_system_id()) { bt_uuid16_create(&uuid, GATT_CHARAC_SYSTEM_ID); - gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, + gatt_db_service_add_characteristic(service, &uuid, enc_perm, GATT_CHR_PROP_READ, device_info_read_system_id_cb, NULL, NULL); @@ -6523,7 +6586,7 @@ static void register_device_info_service(void) data = bt_config_get_fw_rev(); if (data) { bt_uuid16_create(&uuid, GATT_CHARAC_FIRMWARE_REVISION_STRING); - gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, + gatt_db_service_add_characteristic(service, &uuid, GATT_PERM_READ, GATT_CHR_PROP_READ, device_info_read_cb, NULL, @@ -6533,7 +6596,7 @@ static void register_device_info_service(void) data = bt_config_get_hw_rev(); if (data) { bt_uuid16_create(&uuid, GATT_CHARAC_HARDWARE_REVISION_STRING); - gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, + gatt_db_service_add_characteristic(service, &uuid, GATT_PERM_READ, GATT_CHR_PROP_READ, device_info_read_cb, NULL, @@ -6541,14 +6604,14 @@ static void register_device_info_service(void) } bt_uuid16_create(&uuid, GATT_CHARAC_SOFTWARE_REVISION_STRING); - gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, GATT_PERM_READ, + gatt_db_service_add_characteristic(service, &uuid, GATT_PERM_READ, GATT_CHR_PROP_READ, device_info_read_cb, NULL, VERSION); data = bt_config_get_vendor(); if (data) { bt_uuid16_create(&uuid, GATT_CHARAC_MANUFACTURER_NAME_STRING); - gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, + gatt_db_service_add_characteristic(service, &uuid, GATT_PERM_READ, GATT_CHR_PROP_READ, device_info_read_cb, NULL, @@ -6557,31 +6620,31 @@ static void register_device_info_service(void) if (bt_config_get_pnp_source()) { bt_uuid16_create(&uuid, GATT_CHARAC_PNP_ID); - gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, + gatt_db_service_add_characteristic(service, &uuid, GATT_PERM_READ, GATT_CHR_PROP_READ, device_info_read_pnp_id_cb, NULL, NULL); } - gatt_db_service_set_active(gatt_db, srvc_handle, true); + gatt_db_service_set_active(service, true); /* SDP */ bt_uuid16_create(&uuid, 0x180a); - end_handle = gatt_db_get_end_handle(gatt_db, srvc_handle); - dis_sdp_handle = add_sdp_record(&uuid, srvc_handle, end_handle, + gatt_db_attribute_get_service_handles(service, &start_handle, + &end_handle); + dis_sdp_handle = add_sdp_record(&uuid, start_handle, end_handle, "Device Information Service"); if (!dis_sdp_handle) error("gatt: Failed to register DIS SDP record"); } -static void gatt_srvc_change_write_cb(uint16_t handle, uint16_t offset, - const uint8_t *val, size_t len, - uint8_t att_opcode, - bdaddr_t *bdaddr, - void *user_data) +static void gatt_srvc_change_write_cb(struct gatt_db_attribute *attrib, + unsigned int id, uint16_t offset, + const uint8_t *value, size_t len, + uint8_t opcode, bdaddr_t *bdaddr, + void *user_data) { - struct pending_request *entry; struct gatt_device *dev; dev = find_device_by_addr(bdaddr); @@ -6590,29 +6653,25 @@ static void gatt_srvc_change_write_cb(uint16_t handle, uint16_t offset, return; } - entry = queue_find(dev->pending_requests, match_dev_request_by_handle, - UINT_TO_PTR(handle)); - if (!entry) - return; - - entry->state = REQUEST_DONE; - if (!bt_device_is_bonded(bdaddr)) { - entry->error = ATT_ECODE_AUTHORIZATION; + gatt_db_attribute_write_result(attrib, id, + ATT_ECODE_AUTHORIZATION); return; } /* Set services changed indication value */ - bt_store_gatt_ccc(bdaddr, *val); + bt_store_gatt_ccc(bdaddr, *value); + + gatt_db_attribute_write_result(attrib, id, 0); } -static void gatt_srvc_change_read_cb(uint16_t handle, uint16_t offset, - uint8_t att_opcode, bdaddr_t *bdaddr, +static void gatt_srvc_change_read_cb(struct gatt_db_attribute *attrib, + unsigned int id, uint16_t offset, + uint8_t opcode, bdaddr_t *bdaddr, void *user_data) { - struct pending_request *entry; struct gatt_device *dev; - uint16_t ccc = 0; + uint8_t pdu[2]; dev = find_device_by_addr(bdaddr); if (!dev) { @@ -6620,53 +6679,41 @@ static void gatt_srvc_change_read_cb(uint16_t handle, uint16_t offset, return; } - entry = queue_find(dev->pending_requests, match_dev_request_by_handle, - UINT_TO_PTR(handle)); - if (!entry) - return; - - ccc = bt_get_gatt_ccc(&dev->bdaddr); - entry->state = REQUEST_DONE; - - entry->value = new0(uint8_t, 2); - if (!entry->value) { - entry->error = ATT_ECODE_INSUFF_RESOURCES; - - return; - } + put_le16(bt_get_gatt_ccc(&dev->bdaddr), pdu); - entry->length = sizeof(uint16_t); - memcpy(entry->value, &ccc, sizeof(ccc)); + gatt_db_attribute_read_result(attrib, id, 0, pdu, sizeof(pdu)); } static void register_gatt_service(void) { - uint16_t srvc_handle, end_handle; + struct gatt_db_attribute *service; + uint16_t start_handle, end_handle; bt_uuid_t uuid; DBG(""); bt_uuid16_create(&uuid, 0x1801); - srvc_handle = gatt_db_add_service(gatt_db, &uuid, true, 4); + service = gatt_db_add_service(gatt_db, &uuid, true, 4); bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED); - service_changed_handle = gatt_db_add_characteristic(gatt_db, - srvc_handle, &uuid, GATT_PERM_NONE, - GATT_CHR_PROP_INDICATE, NULL, NULL, - NULL); + service_changed_attrib = gatt_db_service_add_characteristic(service, + &uuid, GATT_PERM_NONE, + GATT_CHR_PROP_INDICATE, + NULL, NULL, NULL); bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); - gatt_db_add_char_descriptor(gatt_db, srvc_handle, &uuid, + gatt_db_service_add_descriptor(service, &uuid, GATT_PERM_READ | GATT_PERM_WRITE, gatt_srvc_change_read_cb, gatt_srvc_change_write_cb, NULL); - gatt_db_service_set_active(gatt_db, srvc_handle, true); + gatt_db_service_set_active(service, true); /* SDP */ bt_uuid16_create(&uuid, 0x1801); - end_handle = gatt_db_get_end_handle(gatt_db, srvc_handle); - gatt_sdp_handle = add_sdp_record(&uuid, srvc_handle, end_handle, + gatt_db_attribute_get_service_handles(service, &start_handle, + &end_handle); + gatt_sdp_handle = add_sdp_record(&uuid, start_handle, end_handle, "Generic Attribute Profile"); if (!gatt_sdp_handle) diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c index 5855f5d..bab1202 100644 --- a/src/shared/gatt-db.c +++ b/src/shared/gatt-db.c @@ -45,6 +45,18 @@ struct gatt_db { struct queue *services; }; +struct pending_read { + unsigned int id; + gatt_db_attribute_read_t func; + void *user_data; +}; + +struct pending_write { + unsigned int id; + gatt_db_attribute_write_t func; + void *user_data; +}; + struct gatt_db_attribute { struct gatt_db_service *service; uint16_t handle; @@ -56,6 +68,12 @@ struct gatt_db_attribute { gatt_db_read_t read_func; gatt_db_write_t write_func; void *user_data; + + unsigned int read_id; + struct queue *pending_reads; + + unsigned int write_id; + struct queue *pending_writes; }; struct gatt_db_service { @@ -64,11 +82,17 @@ struct gatt_db_service { struct gatt_db_attribute **attributes; }; -static bool match_service_by_handle(const void *data, const void *user_data) +static void attribute_destroy(struct gatt_db_attribute *attribute) { - const struct gatt_db_service *service = data; + /* Attribute was not initialized by user */ + if (!attribute) + return; + + queue_destroy(attribute->pending_reads, free); + queue_destroy(attribute->pending_writes, free); - return service->attributes[0]->handle == PTR_TO_UINT(user_data); + free(attribute->value); + free(attribute); } static struct gatt_db_attribute *new_attribute(struct gatt_db_service *service, @@ -87,25 +111,25 @@ static struct gatt_db_attribute *new_attribute(struct gatt_db_service *service, attribute->value_len = len; if (len) { attribute->value = malloc0(len); - if (!attribute->value) { - free(attribute); - return NULL; - } + if (!attribute->value) + goto failed; memcpy(attribute->value, val, len); } - return attribute; -} + attribute->pending_reads = queue_new(); + if (!attribute->pending_reads) + goto failed; -static void attribute_destroy(struct gatt_db_attribute *attribute) -{ - /* Attribute was not initialized by user */ - if (!attribute) - return; + attribute->pending_writes = queue_new(); + if (!attribute->pending_reads) + goto failed; - free(attribute->value); - free(attribute); + return attribute; + +failed: + attribute_destroy(attribute); + return NULL; } struct gatt_db *gatt_db_new(void) @@ -162,8 +186,10 @@ static int uuid_to_le(const bt_uuid_t *uuid, uint8_t *dst) return bt_uuid_len(&uuid128); } -uint16_t gatt_db_add_service(struct gatt_db *db, const bt_uuid_t *uuid, - bool primary, uint16_t num_handles) +struct gatt_db_attribute *gatt_db_add_service(struct gatt_db *db, + const bt_uuid_t *uuid, + bool primary, + uint16_t num_handles) { struct gatt_db_service *service; const bt_uuid_t *type; @@ -209,18 +235,21 @@ uint16_t gatt_db_add_service(struct gatt_db *db, const bt_uuid_t *uuid, db->next_handle += num_handles; service->num_handles = num_handles; - return service->attributes[0]->handle; + return service->attributes[0]; } -bool gatt_db_remove_service(struct gatt_db *db, uint16_t handle) +bool gatt_db_remove_service(struct gatt_db *db, + struct gatt_db_attribute *attrib) { struct gatt_db_service *service; - service = queue_remove_if(db->services, match_service_by_handle, - UINT_TO_PTR(handle)); - if (!service) + if (!db || !attrib) return false; + service = attrib->service; + + queue_remove(db->services, service); + gatt_db_service_destroy(service); return true; @@ -245,8 +274,8 @@ static uint16_t get_handle_at_index(struct gatt_db_service *service, return service->attributes[index]->handle; } -static uint16_t update_attribute_handle(struct gatt_db_service *service, - int index) +static struct gatt_db_attribute * +attribute_update(struct gatt_db_service *service, int index) { uint16_t previous_handle; @@ -256,7 +285,7 @@ static uint16_t update_attribute_handle(struct gatt_db_service *service, previous_handle = service->attributes[index - 1]->handle; service->attributes[index]->handle = previous_handle + 1; - return service->attributes[index]->handle; + return service->attributes[index]; } static void set_attribute_data(struct gatt_db_attribute *attribute, @@ -271,27 +300,28 @@ static void set_attribute_data(struct gatt_db_attribute *attribute, attribute->user_data = user_data; } -uint16_t gatt_db_add_characteristic(struct gatt_db *db, uint16_t handle, - const bt_uuid_t *uuid, - uint32_t permissions, - uint8_t properties, - gatt_db_read_t read_func, - gatt_db_write_t write_func, - void *user_data) +struct gatt_db_attribute * +gatt_db_service_add_characteristic(struct gatt_db_attribute *attrib, + const bt_uuid_t *uuid, + uint32_t permissions, + uint8_t properties, + gatt_db_read_t read_func, + gatt_db_write_t write_func, + void *user_data) { - uint8_t value[MAX_CHAR_DECL_VALUE_LEN]; struct gatt_db_service *service; + uint8_t value[MAX_CHAR_DECL_VALUE_LEN]; uint16_t len = 0; int i; - service = queue_find(db->services, match_service_by_handle, - UINT_TO_PTR(handle)); - if (!service) - return 0; + if (!attrib) + return NULL; + + service = attrib->service; i = get_attribute_index(service, 1); if (!i) - return 0; + return NULL; value[0] = properties; len += sizeof(properties); @@ -303,96 +333,96 @@ uint16_t gatt_db_add_characteristic(struct gatt_db *db, uint16_t handle, service->attributes[i] = new_attribute(service, &characteristic_uuid, value, len); if (!service->attributes[i]) - return 0; + return NULL; - update_attribute_handle(service, i++); + attribute_update(service, i++); service->attributes[i] = new_attribute(service, uuid, NULL, 0); if (!service->attributes[i]) { free(service->attributes[i - 1]); - return 0; + return NULL; } set_attribute_data(service->attributes[i], read_func, write_func, permissions, user_data); - return update_attribute_handle(service, i); + return attribute_update(service, i); } -uint16_t gatt_db_add_char_descriptor(struct gatt_db *db, uint16_t handle, - const bt_uuid_t *uuid, - uint32_t permissions, - gatt_db_read_t read_func, - gatt_db_write_t write_func, - void *user_data) +struct gatt_db_attribute * +gatt_db_service_add_descriptor(struct gatt_db_attribute *attrib, + const bt_uuid_t *uuid, + uint32_t permissions, + gatt_db_read_t read_func, + gatt_db_write_t write_func, + void *user_data) { struct gatt_db_service *service; int i; - service = queue_find(db->services, match_service_by_handle, - UINT_TO_PTR(handle)); - if (!service) - return 0; + if (!attrib) + return false; + + service = attrib->service; i = get_attribute_index(service, 0); if (!i) - return 0; + return NULL; service->attributes[i] = new_attribute(service, uuid, NULL, 0); if (!service->attributes[i]) - return 0; + return NULL; set_attribute_data(service->attributes[i], read_func, write_func, permissions, user_data); - return update_attribute_handle(service, i); + return attribute_update(service, i); } -uint16_t gatt_db_add_included_service(struct gatt_db *db, uint16_t handle, - uint16_t included_handle) +struct gatt_db_attribute * +gatt_db_service_add_included(struct gatt_db_attribute *attrib, + struct gatt_db_attribute *include) { - struct gatt_db_service *included_service; + struct gatt_db_service *service, *included; uint8_t value[MAX_INCLUDED_VALUE_LEN]; - uint16_t len = 0; - struct gatt_db_service *service; + uint16_t included_handle, len = 0; int index; - service = queue_find(db->services, match_service_by_handle, - UINT_TO_PTR(handle)); - if (!service) - return 0; + if (!attrib || !include) + return NULL; - included_service = queue_find(db->services, match_service_by_handle, - UINT_TO_PTR(included_handle)); + service = attrib->service; + included = include->service; - if (!included_service) - return 0; + /* Adjust include to point to the first attribute */ + if (include != included->attributes[0]) + include = included->attributes[0]; + + included_handle = include->handle; put_le16(included_handle, &value[len]); len += sizeof(uint16_t); - put_le16(included_handle + included_service->num_handles - 1, - &value[len]); + put_le16(included_handle + included->num_handles - 1, &value[len]); len += sizeof(uint16_t); /* The Service UUID shall only be present when the UUID is a 16-bit * Bluetooth UUID. Vol 2. Part G. 3.2 */ - if (included_service->attributes[0]->value_len == sizeof(uint16_t)) { - memcpy(&value[len], included_service->attributes[0]->value, - included_service->attributes[0]->value_len); - len += included_service->attributes[0]->value_len; + if (include->value_len == sizeof(uint16_t)) { + memcpy(&value[len], include->value, include->value_len); + len += include->value_len; } index = get_attribute_index(service, 0); if (!index) - return 0; + return NULL; service->attributes[index] = new_attribute(service, &included_service_uuid, value, len); if (!service->attributes[index]) - return 0; + return NULL; /* The Attribute Permissions shall be read only and not require * authentication or authorization. Vol 2. Part G. 3.2 @@ -401,20 +431,15 @@ uint16_t gatt_db_add_included_service(struct gatt_db *db, uint16_t handle, */ set_attribute_data(service->attributes[index], NULL, NULL, 0, NULL); - return update_attribute_handle(service, index); + return attribute_update(service, index); } -bool gatt_db_service_set_active(struct gatt_db *db, uint16_t handle, - bool active) +bool gatt_db_service_set_active(struct gatt_db_attribute *attrib, bool active) { - struct gatt_db_service *service; - - service = queue_find(db->services, match_service_by_handle, - UINT_TO_PTR(handle)); - if (!service) + if (!attrib) return false; - service->active = active; + attrib->service->active = active; return true; } @@ -465,8 +490,7 @@ static void read_by_group_type(void *data, void *user_data) return; } - queue_push_tail(search_data->queue, - UINT_TO_PTR(service->attributes[0]->handle)); + queue_push_tail(search_data->queue, service->attributes[0]); } void gatt_db_read_by_group_type(struct gatt_db *db, uint16_t start_handle, @@ -516,8 +540,7 @@ static void find_by_type(void *data, void *user_data) if (bt_uuid_cmp(&search_data->uuid, &attribute->uuid)) continue; - queue_push_tail(search_data->queue, - UINT_TO_PTR(attribute->handle)); + queue_push_tail(search_data->queue, attribute); } } @@ -567,8 +590,7 @@ static void read_by_type(void *data, void *user_data) if (bt_uuid_cmp(&search_data->uuid, &attribute->uuid)) continue; - queue_push_tail(search_data->queue, - UINT_TO_PTR(attribute->handle)); + queue_push_tail(search_data->queue, attribute); } } @@ -619,8 +641,7 @@ static void find_information(void *data, void *user_data) if (attribute->handle > search_data->end_handle) return; - queue_push_tail(search_data->queue, - UINT_TO_PTR(attribute->handle)); + queue_push_tail(search_data->queue, attribute); } } @@ -649,164 +670,6 @@ static bool find_service_for_handle(const void *data, const void *user_data) return (start <= handle) && (handle < end); } -bool gatt_db_read(struct gatt_db *db, uint16_t handle, uint16_t offset, - uint8_t att_opcode, bdaddr_t *bdaddr, - uint8_t **value, int *length) -{ - struct gatt_db_service *service; - uint16_t service_handle; - struct gatt_db_attribute *a; - - if (!value || !length) - return false; - - service = queue_find(db->services, find_service_for_handle, - UINT_TO_PTR(handle)); - if (!service) - return false; - - service_handle = service->attributes[0]->handle; - - a = service->attributes[handle - service_handle]; - if (!a) - return false; - - /* - * We call callback, and set length to -1, to notify user that callback - * has been called. Otherwise we set length to value length in database. - */ - if (a->read_func) { - *value = NULL; - *length = -1; - a->read_func(handle, offset, att_opcode, bdaddr, a->user_data); - } else { - if (offset > a->value_len) - return false; - - *value = &a->value[offset]; - *length = a->value_len - offset; - } - - return true; -} - -bool gatt_db_write(struct gatt_db *db, uint16_t handle, uint16_t offset, - const uint8_t *value, size_t len, - uint8_t att_opcode, bdaddr_t *bdaddr) -{ - struct gatt_db_service *service; - uint16_t service_handle; - struct gatt_db_attribute *a; - - service = queue_find(db->services, find_service_for_handle, - UINT_TO_PTR(handle)); - if (!service) - return false; - - service_handle = service->attributes[0]->handle; - - a = service->attributes[handle - service_handle]; - if (!a || !a->write_func) - return false; - - a->write_func(handle, offset, value, len, att_opcode, bdaddr, - a->user_data); - - return true; -} - -const bt_uuid_t *gatt_db_get_attribute_type(struct gatt_db *db, - uint16_t handle) -{ - struct gatt_db_service *service; - struct gatt_db_attribute *attribute; - uint16_t service_handle; - - service = queue_find(db->services, find_service_for_handle, - UINT_TO_PTR(handle)); - if (!service) - return NULL; - - service_handle = service->attributes[0]->handle; - - attribute = service->attributes[handle - service_handle]; - if (!attribute) - return NULL; - - return &attribute->uuid; -} - -uint16_t gatt_db_get_end_handle(struct gatt_db *db, uint16_t handle) -{ - struct gatt_db_service *service; - - service = queue_find(db->services, find_service_for_handle, - UINT_TO_PTR(handle)); - if (!service) - return 0; - - return service->attributes[0]->handle + service->num_handles - 1; -} - -bool gatt_db_get_service_uuid(struct gatt_db *db, uint16_t handle, - bt_uuid_t *uuid) -{ - struct gatt_db_service *service; - - service = queue_find(db->services, find_service_for_handle, - UINT_TO_PTR(handle)); - if (!service) - return false; - - if (service->attributes[0]->value_len == 2) { - uint16_t value; - - value = get_le16(service->attributes[0]->value); - bt_uuid16_create(uuid, value); - - return true; - } - - if (service->attributes[0]->value_len == 16) { - uint128_t value; - - bswap_128(service->attributes[0]->value, &value); - bt_uuid128_create(uuid, value); - - return true; - } - - return false; -} - -bool gatt_db_get_attribute_permissions(struct gatt_db *db, uint16_t handle, - uint32_t *permissions) -{ - struct gatt_db_attribute *attribute; - struct gatt_db_service *service; - uint16_t service_handle; - - service = queue_find(db->services, find_service_for_handle, - UINT_TO_PTR(handle)); - if (!service) - return false; - - service_handle = service->attributes[0]->handle; - - /* - * We can safely get attribute from attributes array with offset, - * because find_service_for_handle() check if given handle is - * in service range. - */ - attribute = service->attributes[handle - service_handle]; - if (!attribute) - return false; - - *permissions = attribute->permissions; - return true; - -} - struct gatt_db_attribute *gatt_db_get_attribute(struct gatt_db *db, uint16_t handle) { @@ -920,8 +783,19 @@ bool gatt_db_attribute_read(struct gatt_db_attribute *attrib, uint16_t offset, return false; if (attrib->read_func) { - /* TODO: Pass callback function to read_func */ - attrib->read_func(attrib->handle, offset, opcode, bdaddr, + struct pending_read *p; + + p = new0(struct pending_read, 1); + if (!p) + return false; + + p->id = ++attrib->read_id; + p->func = func; + p->user_data = user_data; + + queue_push_tail(attrib->pending_reads, p); + + attrib->read_func(attrib, p->id, offset, opcode, bdaddr, attrib->user_data); return true; } @@ -938,6 +812,35 @@ bool gatt_db_attribute_read(struct gatt_db_attribute *attrib, uint16_t offset, return true; } +static bool find_pending(const void *a, const void *b) +{ + const struct pending_read *p = a; + unsigned int id = PTR_TO_UINT(b); + + return p->id == id; +} + +bool gatt_db_attribute_read_result(struct gatt_db_attribute *attrib, + unsigned int id, int err, + const uint8_t *value, size_t length) +{ + struct pending_read *p; + + if (!attrib || !id) + return false; + + p = queue_remove_if(attrib->pending_reads, find_pending, + UINT_TO_PTR(id)); + if (!p) + return false; + + p->func(attrib, err, value, length, p->user_data); + + free(p); + + return true; +} + bool gatt_db_attribute_write(struct gatt_db_attribute *attrib, uint16_t offset, const uint8_t *value, size_t len, uint8_t opcode, bdaddr_t *bdaddr, @@ -948,7 +851,19 @@ bool gatt_db_attribute_write(struct gatt_db_attribute *attrib, uint16_t offset, return false; if (attrib->write_func) { - attrib->write_func(attrib->handle, offset, value, len, opcode, + struct pending_write *p; + + p = new0(struct pending_write, 1); + if (!p) + return false; + + p->id = ++attrib->write_id; + p->func = func; + p->user_data = user_data; + + queue_push_tail(attrib->pending_writes, p); + + attrib->write_func(attrib, p->id, offset, value, len, opcode, bdaddr, attrib->user_data); return true; } @@ -971,3 +886,23 @@ bool gatt_db_attribute_write(struct gatt_db_attribute *attrib, uint16_t offset, return true; } + +bool gatt_db_attribute_write_result(struct gatt_db_attribute *attrib, + unsigned int id, int err) +{ + struct pending_write *p; + + if (!attrib || !id) + return false; + + p = queue_remove_if(attrib->pending_writes, find_pending, + UINT_TO_PTR(id)); + if (!p) + return false; + + p->func(attrib, err, p->user_data); + + free(p); + + return true; +} diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h index 15be67f..9c71814 100644 --- a/src/shared/gatt-db.h +++ b/src/shared/gatt-db.h @@ -22,43 +22,52 @@ */ struct gatt_db; +struct gatt_db_attribute; struct gatt_db *gatt_db_new(void); void gatt_db_destroy(struct gatt_db *db); -uint16_t gatt_db_add_service(struct gatt_db *db, const bt_uuid_t *uuid, - bool primary, uint16_t num_handles); -bool gatt_db_remove_service(struct gatt_db *db, uint16_t handle); +struct gatt_db_attribute *gatt_db_add_service(struct gatt_db *db, + const bt_uuid_t *uuid, + bool primary, + uint16_t num_handles); + +bool gatt_db_remove_service(struct gatt_db *db, + struct gatt_db_attribute *attrib); -typedef void (*gatt_db_read_t) (uint16_t handle, uint16_t offset, - uint8_t att_opcode, bdaddr_t *bdaddr, +typedef void (*gatt_db_read_t) (struct gatt_db_attribute *attrib, + unsigned int id, uint16_t offset, + uint8_t opcode, bdaddr_t *bdaddr, void *user_data); -typedef void (*gatt_db_write_t) (uint16_t handle, uint16_t offset, +typedef void (*gatt_db_write_t) (struct gatt_db_attribute *attrib, + unsigned int id, uint16_t offset, const uint8_t *value, size_t len, - uint8_t att_opcode, bdaddr_t *bdaddr, + uint8_t opcode, bdaddr_t *bdaddr, void *user_data); -uint16_t gatt_db_add_characteristic(struct gatt_db *db, uint16_t handle, - const bt_uuid_t *uuid, - uint32_t permissions, - uint8_t properties, - gatt_db_read_t read_func, - gatt_db_write_t write_func, - void *user_data); +struct gatt_db_attribute * +gatt_db_service_add_characteristic(struct gatt_db_attribute *attrib, + const bt_uuid_t *uuid, + uint32_t permissions, + uint8_t properties, + gatt_db_read_t read_func, + gatt_db_write_t write_func, + void *user_data); -uint16_t gatt_db_add_char_descriptor(struct gatt_db *db, uint16_t handle, - const bt_uuid_t *uuid, - uint32_t permissions, - gatt_db_read_t read_func, - gatt_db_write_t write_func, - void *user_data); +struct gatt_db_attribute * +gatt_db_service_add_descriptor(struct gatt_db_attribute *attrib, + const bt_uuid_t *uuid, + uint32_t permissions, + gatt_db_read_t read_func, + gatt_db_write_t write_func, + void *user_data); -uint16_t gatt_db_add_included_service(struct gatt_db *db, uint16_t handle, - uint16_t included_handle); +struct gatt_db_attribute * +gatt_db_service_add_included(struct gatt_db_attribute *attrib, + struct gatt_db_attribute *include); -bool gatt_db_service_set_active(struct gatt_db *db, uint16_t handle, - bool active); +bool gatt_db_service_set_active(struct gatt_db_attribute *attrib, bool active); void gatt_db_read_by_group_type(struct gatt_db *db, uint16_t start_handle, uint16_t end_handle, @@ -79,25 +88,6 @@ void gatt_db_find_information(struct gatt_db *db, uint16_t start_handle, uint16_t end_handle, struct queue *queue); -bool gatt_db_read(struct gatt_db *db, uint16_t handle, uint16_t offset, - uint8_t att_opcode, bdaddr_t *bdaddr, - uint8_t **value, int *length); - -bool gatt_db_write(struct gatt_db *db, uint16_t handle, uint16_t offset, - const uint8_t *value, size_t len, - uint8_t att_opcode, bdaddr_t *bdaddr); - -const bt_uuid_t *gatt_db_get_attribute_type(struct gatt_db *db, - uint16_t handle); - -uint16_t gatt_db_get_end_handle(struct gatt_db *db, uint16_t handle); -bool gatt_db_get_service_uuid(struct gatt_db *db, uint16_t handle, - bt_uuid_t *uuid); - -bool gatt_db_get_attribute_permissions(struct gatt_db *db, uint16_t handle, - uint32_t *permissions); - -struct gatt_db_attribute; struct gatt_db_attribute *gatt_db_get_attribute(struct gatt_db *db, uint16_t handle); @@ -117,13 +107,17 @@ bool gatt_db_attribute_get_permissions(struct gatt_db_attribute *attrib, uint32_t *permissions); typedef void (*gatt_db_attribute_read_t) (struct gatt_db_attribute *attrib, - int err, uint8_t *value, size_t length, - void *user_data); + int err, const uint8_t *value, + size_t length, void *user_data); bool gatt_db_attribute_read(struct gatt_db_attribute *attrib, uint16_t offset, uint8_t opcode, bdaddr_t *bdaddr, gatt_db_attribute_read_t func, void *user_data); +bool gatt_db_attribute_read_result(struct gatt_db_attribute *attrib, + unsigned int id, int err, + const uint8_t *value, size_t length); + typedef void (*gatt_db_attribute_write_t) (struct gatt_db_attribute *attrib, int err, void *user_data); @@ -132,3 +126,6 @@ bool gatt_db_attribute_write(struct gatt_db_attribute *attrib, uint16_t offset, uint8_t opcode, bdaddr_t *bdaddr, gatt_db_attribute_write_t func, void *user_data); + +bool gatt_db_attribute_write_result(struct gatt_db_attribute *attrib, + unsigned int id, int err); diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c index 657b564..18f82c4 100644 --- a/src/shared/gatt-server.c +++ b/src/shared/gatt-server.c @@ -21,6 +21,8 @@ * */ +#include + #include "src/shared/att.h" #include "lib/uuid.h" #include "src/shared/queue.h" @@ -91,31 +93,41 @@ static bool get_uuid_le(const uint8_t *uuid, size_t len, bt_uuid_t *out_uuid) return false; } +static void attribute_read_cb(struct gatt_db_attribute *attrib, int err, + const uint8_t *value, size_t length, + void *user_data) +{ + struct iovec *iov = user_data; + + iov->iov_base = (void *) value; + iov->iov_len = length; +} + static bool encode_read_by_grp_type_rsp(struct gatt_db *db, struct queue *q, uint16_t mtu, uint8_t *pdu, uint16_t *len) { int iter = 0; uint16_t start_handle, end_handle; - uint8_t *value; - int value_len; + struct iovec value; uint8_t data_val_len; *len = 0; while (queue_peek_head(q)) { - start_handle = PTR_TO_UINT(queue_pop_head(q)); - value = NULL; - value_len = 0; + 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_read(db, start_handle, 0, + if (!gatt_db_attribute_read(attrib, 0, BT_ATT_OP_READ_BY_GRP_TYPE_REQ, - NULL, &value, - &value_len) || value_len < 0) + NULL, attribute_read_cb, + &value) || !value.iov_len) return false; /* @@ -124,21 +136,22 @@ static bool encode_read_by_grp_type_rsp(struct gatt_db *db, struct queue *q, * value is seen. */ if (iter == 0) { - data_val_len = value_len; + data_val_len = value.iov_len; pdu[0] = data_val_len + 4; iter++; - } else if (value_len != data_val_len) + } else if (value.iov_len != data_val_len) break; /* Stop if this unit would surpass the MTU */ if (iter + data_val_len + 4 > mtu) break; - end_handle = gatt_db_get_end_handle(db, start_handle); + 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, value_len); + memcpy(pdu + iter + 4, value.iov_base, value.iov_len); iter += data_val_len + 4; } -- 1.9.3