diff --git a/android/gatt.c b/android/gatt.c
index 67a1330..7f03af5 100644
--- a/android/gatt.c
+++ b/android/gatt.c
int ref;
int conn_cnt;
+
+ struct queue *pending_requests;
};
struct app_connection {
send_app_connect_notify(conn, GATT_FAILURE);
}
+#define READ_INIT -3
+#define READ_PENDING -2
+#define READ_FAILED -1
+
+struct pending_request {
+ uint16_t handle;
+ int length;
+ uint8_t *value;
+ uint16_t offset;
+};
+
+static void destroy_pending_request(void *data)
+{
+ struct pending_request *entry = data;
+
+ free(entry->value);
+ free(entry);
+}
+
static void destroy_device(void *data)
{
struct gatt_device *dev = data;
return;
queue_destroy(dev->services, destroy_service);
+ queue_destroy(dev->pending_requests, destroy_pending_request);
free(dev);
}
return NULL;
}
+ dev->pending_requests = queue_new();
+ if (!dev->pending_requests) {
+ error("gatt: Failed to allocate memory for client");
+ destroy_device(dev);
+ return NULL;
+ }
+
if (!queue_push_head(gatt_devices, dev)) {
error("gatt: Cannot push device to queue");
destroy_device(dev);
HAL_OP_GATT_SERVER_ADD_INC_SERVICE, status);
}
+static void send_dev_pending_response(struct gatt_device *device,
+ uint8_t opcode)
+{
+ uint8_t rsp[ATT_DEFAULT_LE_MTU];
+ struct pending_request *val;
+ uint16_t len = 0;
+ uint8_t error = 0;
+
+ switch (opcode) {
+ case ATT_OP_READ_BLOB_REQ:
+ val = queue_pop_head(device->pending_requests);
+ if (!val || val->length < 0) {
+ error = ATT_ECODE_ATTR_NOT_FOUND;
+ goto done;
+ }
+
+ len = enc_read_blob_resp(val->value, val->length, val->offset,
+ rsp, sizeof(rsp));
+ destroy_pending_request(val);
+ break;
+ case ATT_OP_READ_REQ:
+ val = queue_pop_head(device->pending_requests);
+ if (!val || val->length < 0) {
+ error = ATT_ECODE_ATTR_NOT_FOUND;
+ goto done;
+ }
+
+ len = enc_read_resp(val->value, val->length, rsp, sizeof(rsp));
+ destroy_pending_request(val);
+ break;
+ default:
+ break;
+ }
+
+done:
+ if (!len)
+ len = enc_error_resp(opcode, 0x0000, error, rsp,
+ ATT_DEFAULT_LE_MTU);
+
+ g_attrib_send(device->attrib, 0, rsp, len, NULL, NULL, NULL);
+
+ queue_remove_all(device->pending_requests, NULL, NULL,
+ destroy_pending_request);
+}
+
+struct request_processing_data {
+ uint8_t opcode;
+ struct gatt_device *device;
+};
+
+static bool match_pending_dev_request(const void *data, const void *user_data)
+{
+ const struct pending_request *pending_request = data;
+
+ return pending_request->length == READ_PENDING;
+}
+
+static bool match_dev_request_by_handle(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;
+}
+
+static void read_requested_attributes(void *data, void *user_data)
+{
+ struct pending_request *resp_data = data;
+ struct request_processing_data *process_data = user_data;
+ uint8_t *value;
+ int value_len;
+
+ if (!gatt_db_read(gatt_db, resp_data->handle,
+ resp_data->offset,
+ process_data->opcode,
+ &process_data->device->bdaddr,
+ &value, &value_len))
+ resp_data->length = READ_FAILED;
+
+ /* We have value here already if no callback will be called */
+ if (value_len >= 0) {
+ resp_data->value = malloc0(value_len);
+ if (!resp_data->value) {
+ /* If data cannot be copied, act like when read fails */
+ resp_data->length = READ_FAILED;
+ return;
+ }
+
+ memcpy(resp_data->value, value, value_len);
+ resp_data->length = value_len;
+ } else {
+ resp_data->length = READ_PENDING;
+ }
+}
+
+static void process_dev_pending_requests(struct gatt_device *device,
+ uint8_t att_opcode)
+{
+ struct request_processing_data process_data;
+
+ process_data.device = device;
+ process_data.opcode = att_opcode;
+
+ /* Process pending requests and prepare response */
+ queue_foreach(device->pending_requests, read_requested_attributes,
+ &process_data);
+
+ if (!queue_find(device->pending_requests,
+ match_pending_dev_request, NULL))
+ send_dev_pending_response(device, att_opcode);
+}
+
static void send_gatt_response(uint8_t opcode, uint16_t handle,
uint16_t offset, uint8_t status,
uint16_t len, const uint8_t *data,
{
uint16_t handle;
uint16_t len;
- uint16_t offset = 0;
+ uint16_t offset;
+ struct pending_request *data;
DBG("");
len = dec_read_req(cmd, cmd_len, &handle);
if (!len)
return ATT_ECODE_INVALID_PDU;
+ offset = 0;
break;
default:
error("gatt: Unexpected read type 0x%02x", cmd[0]);
return ATT_ECODE_REQ_NOT_SUPP;
}
- if (!gatt_db_read(gatt_db, handle, offset, cmd[0], &dev->bdaddr, NULL,
- NULL))
- return ATT_ECODE_UNLIKELY;
+ data = new0(struct pending_request, 1);
+ if (!data)
+ return ATT_ECODE_INSUFF_RESOURCES;
+
+ data->offset = offset;
+ data->handle = handle;
+ data->length = READ_INIT;
+ if (!queue_push_tail(dev->pending_requests, data)) {
+ free(data);
+ return ATT_ECODE_INSUFF_RESOURCES;
+ }
+
+ process_dev_pending_requests(dev, cmd[0]);
return 0;
}
case ATT_OP_READ_REQ:
case ATT_OP_READ_BLOB_REQ:
status = read_request(ipdu, len, dev);
- if (!status)
- /* Response will be sent in callback */
- return;
break;
case ATT_OP_MTU_REQ:
status = mtu_att_handle(ipdu, len, opdu, sizeof(opdu), dev,
length = enc_error_resp(ipdu[0], 0x0000, status, opdu,
ATT_DEFAULT_LE_MTU);
- g_attrib_send(dev->attrib, 0, opdu, length, NULL, NULL, NULL);
+ if (length)
+ g_attrib_send(dev->attrib, 0, opdu, length, NULL, NULL, NULL);
}
static void create_listen_connections(void *data, void *user_data)
static void gap_read_cb(uint16_t handle, uint16_t offset, uint8_t att_opcode,
bdaddr_t *bdaddr, void *user_data)
{
- uint8_t pdu[ATT_DEFAULT_LE_MTU];
- uint16_t len;
+ struct pending_request *entry;
struct gatt_device *dev;
DBG("");
return;
}
+ entry = queue_find(dev->pending_requests, match_dev_request_by_handle,
+ UINT_TO_PTR(handle));
+ if (!entry)
+ return;
+
if (handle == gap_srvc_data.dev_name) {
const char *name = bt_get_adapter_name();
- len = enc_read_resp((uint8_t *)name, strlen(name),
- pdu, sizeof(pdu));
- goto done;
- }
+ entry->value = malloc0(strlen(name));
+ if (!entry->value) {
+ queue_remove(dev->pending_requests, entry);
+ free(entry);
+ return;
+ }
- if (handle == gap_srvc_data.appear) {
- uint8_t val[2];
- put_le16(APPEARANCE_GENERIC_PHONE, val);
- len = enc_read_resp(val, sizeof(val), pdu, sizeof(pdu));
- goto done;
- }
+ entry->length = strlen(name);
+ memcpy(entry->value, bt_get_adapter_name(), entry->length);
+ } else if (handle == gap_srvc_data.appear) {
+ entry->value = malloc0(2);
+ if (!entry->value) {
+ queue_remove(dev->pending_requests, entry);
+ free(entry);
+ return;
+ }
- if (handle == gap_srvc_data.priv) {
- uint8_t val = PERIPHERAL_PRIVACY_DISABLE;
- len = enc_read_resp(&val, sizeof(val), pdu, sizeof(pdu));
- goto done;
- }
+ put_le16(APPEARANCE_GENERIC_PHONE, entry->value);
+ entry->length = sizeof(uint8_t) * 2;
+ } else if (handle == gap_srvc_data.priv) {
+ entry->value = malloc0(1);
+ if (!entry->value) {
+ queue_remove(dev->pending_requests, entry);
+ free(entry);
+ return;
+ }
- error("gatt: Unknown handle 0x%02x", handle);
- len = enc_error_resp(ATT_OP_READ_REQ, handle,
- ATT_ECODE_UNLIKELY, pdu, sizeof(pdu));
+ *entry->value = PERIPHERAL_PRIVACY_DISABLE;
+ entry->length = sizeof(uint8_t);
+ }
-done:
- g_attrib_send(dev->attrib, 0, pdu, len, NULL, NULL, NULL);
+ entry->offset = offset;
}
static void register_gap_service(void)
uint8_t att_opcode, bdaddr_t *bdaddr,
void *user_data)
{
- uint8_t pdu[ATT_DEFAULT_LE_MTU];
+ struct pending_request *entry;
struct gatt_device *dev;
char *buf = user_data;
- uint16_t len;
dev = find_device_by_addr(bdaddr);
if (!dev) {
return;
}
- len = enc_read_resp((uint8_t *) buf, strlen(buf), pdu, sizeof(pdu));
+ 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) {
+ queue_remove(dev->pending_requests, entry);
+ free(entry);
+ return;
+ }
- g_attrib_send(dev->attrib, 0, pdu, len, NULL, NULL, NULL);
+ entry->length = strlen(buf);
+ memcpy(entry->value, buf, entry->length);
+ entry->offset = offset;
}
static void register_device_info_service(void)