diff mbox series

[BlueZ,1/2] monitor/att: Attempt to insert discovered attributes

Message ID 20230505203156.2561265-1-luiz.dentz@gmail.com
State New
Headers show
Series [BlueZ,1/2] monitor/att: Attempt to insert discovered attributes | expand

Commit Message

Luiz Augusto von Dentz May 5, 2023, 8:31 p.m. UTC
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This attempts to insert discovered attributes into monitor gatt_db
instance if their respective discover procedures are used which enables
decoding traces injected by user via unit testing:

> sudo unit/test-bap -m -s "34

= test-bap: BAP/UCL/SCC/BV-034-C [UCL SNK Config Codec, VS] - run
> test-bap: User Data RX
      ATT: Read Request (0x0a) len 2
        Handle: 0x0003 Type: Sink PAC (0x2bc9)
< test-bap: User Data TX
      ATT: Read Response (0x0b) len 8
        Handle: 0x0003 Type: Sink PAC (0x2bc9)
        Value: 01ff010001000000
          Number of PAC(s): 1
          PAC #0:
            Codec: Vendor specific (0xff)
            Codec Company ID: Nokia Mobile Phones (0x0001)
            Codec Vendor ID: 0x0001
> test-bap: User Data RX
      ATT: Read Request (0x0a) len 2
        Handle: 0x0006 Type: Sink Audio Locations (0x2bca)
< test-bap: User Data TX
      ATT: Read Response (0x0b) len 4
        Handle: 0x0006 Type: Sink Audio Locations (0x2bca)
        Value: 03000000
           Location: 0x00000003
              Front Left (0x00000001)
              Front Right (0x00000002)
> test-bap: User Data RX
      ATT: Read Request (0x0a) len 2
        Handle: 0x0009 Type: Source PAC (0x2bcb)
< test-bap: User Data TX
      ATT: Read Response (0x0b) len 8
        Handle: 0x0009 Type: Source PAC (0x2bcb)
        Value: 01ff010001000000
          Number of PAC(s): 1
          PAC #0:
            Codec: Vendor specific (0xff)
            Codec Company ID: Nokia Mobile Phones (0x0001)
            Codec Vendor ID: 0x0001
> test-bap: User Data RX
      ATT: Read Request (0x0a) len 2
        Handle: 0x000c Type: Source Audio Locations (0x2bcc)
< test-bap: User Data TX
      ATT: Read Response (0x0b) len 4
        Handle: 0x000c Type: Source Audio Locations (0x2bcc)
        Value: 03000000
           Location: 0x00000003
              Front Left (0x00000001)
              Front Right (0x00000002)
> test-bap: User Data RX
      ATT: Read Request (0x0a) len 2
        Handle: 0x000f Type: Available Audio Contexts (0x2bcd)
< test-bap: User Data TX
      ATT: Read Response (0x0b) len 4
        Handle: 0x000f Type: Available Audio Contexts (0x2bcd)
        Value: ff0f0e00
          Sink Context: 0x0fff
            Unspecified (0x0001)
            Conversational (0x0002)
            Media (0x0004)
            Game (0x0008)
            Instructional (0x0010)
            Voice Assistants (0x0020)
            Live (0x0040)
            Sound Effects (0x0080)
            Notifications (0x0100)
            Ringtone (0x0200)
            Alerts (0x0400)
            Emergency alarm (0x0800)
          Source Context: 0x000e
            Conversational (0x0002)
            Media (0x0004)
            Game (0x0008)
> test-bap: User Data RX
      ATT: Read Request (0x0a) len 2
        Handle: 0x0012 Type: Supported Audio Contexts (0x2bce)
< test-bap: User Data TX
      ATT: Read Response (0x0b) len 4
        Handle: 0x0012 Type: Supported Audio Contexts (0x2bce)
        Value: ff0f0e00
          Sink Context: 0x0fff
            Unspecified (0x0001)
            Conversational (0x0002)
            Media (0x0004)
            Game (0x0008)
            Instructional (0x0010)
            Voice Assistants (0x0020)
            Live (0x0040)
            Sound Effects (0x0080)
            Notifications (0x0100)
            Ringtone (0x0200)
            Alerts (0x0400)
            Emergency alarm (0x0800)
          Source Context: 0x000e
            Conversational (0x0002)
            Media (0x0004)
            Game (0x0008)
> test-bap: User Data RX
      ATT: Read Request (0x0a) len 2
        Handle: 0x0016 Type: Sink ASE (0x2bc4)
< test-bap: User Data TX
      ATT: Read Response (0x0b) len 2
        Handle: 0x0016 Type: Sink ASE (0x2bc4)
        Value: 0100
            ASE ID: 1
            State: Idle (0x00)
> test-bap: User Data RX
      ATT: Write Request (0x12) len 4
        Handle: 0x0017
          Data: 0100
< test-bap: User Data TX
      ATT: Write Response (0x13) len 0
> test-bap: User Data RX
      ATT: Read Request (0x0a) len 2
        Handle: 0x0019 Type: Sink ASE (0x2bc4)
< test-bap: User Data TX
      ATT: Read Response (0x0b) len 2
        Handle: 0x0019 Type: Sink ASE (0x2bc4)
        Value: 0200
            ASE ID: 2
            State: Idle (0x00)
> test-bap: User Data RX
      ATT: Write Request (0x12) len 4
        Handle: 0x001a
          Data: 0100
< test-bap: User Data TX
      ATT: Write Response (0x13) len 0
> test-bap: User Data RX
      ATT: Read Request (0x0a) len 2
        Handle: 0x001c Type: Source ASE (0x2bc5)
< test-bap: User Data TX
      ATT: Read Response (0x0b) len 2
        Handle: 0x001c Type: Source ASE (0x2bc5)
        Value: 0300
            ASE ID: 3
            State: Idle (0x00)
> test-bap: User Data RX
      ATT: Write Request (0x12) len 4
        Handle: 0x001d
          Data: 0100
< test-bap: User Data TX
      ATT: Write Response (0x13) len 0
> test-bap: User Data RX
      ATT: Read Request (0x0a) len 2
        Handle: 0x001f Type: Source ASE (0x2bc5)
< test-bap: User Data TX
      ATT: Read Response (0x0b) len 2
        Handle: 0x001f Type: Source ASE (0x2bc5)
        Value: 0400
            ASE ID: 4
            State: Idle (0x00)
> test-bap: User Data RX
      ATT: Write Request (0x12) len 4
        Handle: 0x0020
          Data: 0100
< test-bap: User Data TX
      ATT: Write Response (0x13) len 0
> test-bap: User Data RX
      ATT: Write Request (0x12) len 4
        Handle: 0x0023
          Data: 0100
< test-bap: User Data TX
      ATT: Write Response (0x13) len 0
> test-bap: User Data RX
      ATT: Write Command (0x52) len 13
        Handle: 0x0022 Type: ASE Control Point (0x2bc6)
          Data: 0101030202ff0100010000
            Opcode: Codec Configuration (0x01)
            Number of ASE(s): 1
            ASE: #0
            ASE ID: 0x03
            Target Latency: Balance Latency/Reliability (0x02)
            PHY: 0x02
            LE 2M PHY (0x02)
            Codec: Vendor specific (0xff)
            Codec Company ID: Nokia Mobile Phones (0x0001)
            Codec Vendor ID: 0x0001
< test-bap: User Data TX
      ATT: Handle Value Notification (0x1b) len 7
        Handle: 0x0022 Type: ASE Control Point (0x2bc6)
          Data: 0101030000
            Opcode: Codec Configuration (0x01)
            Number of ASE(s): 1
            ASE: #0
            ASE ID: 0x03
            ASE Response Code: Success (0x00)
            ASE Response Reason: None (0x00)
< test-bap: User Data TX
      ATT: Handle Value Notification (0x1b) len 27
        Handle: 0x001c Type: Source ASE (0x2bc5)
          Data: 03010102010a00204e00409c00204e00409c00ff0100010000
            ASE ID: 3
            State: Codec Configured (0x01)
            Framing: Unframed PDUs not supported (0x01)
            PHY: 0x02
            LE 2M PHY preffered (0x02)
            RTN: 1
            Max Transport Latency: 10
            Presentation Delay Min: 20000 us
            Presentation Delay Max: 40000 us
            Preferred Presentation Delay Min: 20000 us
            Preferred Presentation Delay Max: 40000 us
            Codec: Vendor specific (0xff)
            Codec Company ID: Nokia Mobile Phones (0x0001)
            Codec Vendor ID: 0x0001
---
 monitor/att.c | 538 ++++++++++++++++++++++++++++++++------------------
 1 file changed, 345 insertions(+), 193 deletions(-)

Comments

bluez.test.bot@gmail.com May 5, 2023, 10 p.m. UTC | #1
This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=745389

---Test result---

Test Summary:
CheckPatch                    FAIL      1.55 seconds
GitLint                       PASS      0.64 seconds
BuildEll                      PASS      32.30 seconds
BluezMake                     PASS      1012.15 seconds
MakeCheck                     PASS      13.45 seconds
MakeDistcheck                 PASS      186.69 seconds
CheckValgrind                 PASS      300.63 seconds
CheckSmatch                   WARNING   403.87 seconds
bluezmakeextell               PASS      123.37 seconds
IncrementalBuild              PASS      1661.30 seconds
ScanBuild                     PASS      1239.79 seconds

Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
[BlueZ,2/2] monitor: Fix misaligment errors
WARNING:PREFER_DEFINED_ATTRIBUTE_MACRO: Prefer __packed over __attribute__((packed))
#106: FILE: monitor/packet.c:11948:
+} __attribute__((packed));

/github/workspace/src/src/13233010.patch total: 0 errors, 1 warnings, 17 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
      mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/src/13233010.patch has style problems, please review.

NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO

NOTE: If any of the errors are false positives, please report
      them to the maintainer, see CHECKPATCH in MAINTAINERS.


##############################
Test: CheckSmatch - WARNING
Desc: Run smatch tool with source
Output:
monitor/att.c: note: in included file:monitor/display.h:82:26: warning: Variable length array is used.monitor/packet.c: note: in included file:monitor/display.h:82:26: warning: Variable length array is used.monitor/packet.c:1800:26: warning: Variable length array is used.monitor/packet.c: note: in included file:monitor/bt.h:3552:52: warning: array of flexible structuresmonitor/bt.h:3540:40: warning: array of flexible structures


---
Regards,
Linux Bluetooth
patchwork-bot+bluetooth@kernel.org May 9, 2023, 12:20 a.m. UTC | #2
Hello:

This series was applied to bluetooth/bluez.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:

On Fri,  5 May 2023 13:31:55 -0700 you wrote:
> From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
> 
> This attempts to insert discovered attributes into monitor gatt_db
> instance if their respective discover procedures are used which enables
> decoding traces injected by user via unit testing:
> 
> > sudo unit/test-bap -m -s "34
> 
> [...]

Here is the summary with links:
  - [BlueZ,1/2] monitor/att: Attempt to insert discovered attributes
    (no matching commit)
  - [BlueZ,2/2] monitor: Fix misaligment errors
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=acfa41dedb47

You are awesome, thank you!
diff mbox series

Patch

diff --git a/monitor/att.c b/monitor/att.c
index e67887861448..0e12ee352078 100644
--- a/monitor/att.c
+++ b/monitor/att.c
@@ -365,10 +365,11 @@  static void att_error_response(const struct l2cap_frame *frame)
 	print_field("Handle: 0x%4.4x", le16_to_cpu(pdu->handle));
 	print_field("Error: %s (0x%2.2x)", str, pdu->error);
 
-	/* Read/Read By Type may create a read object which needs to be dequeued
-	 * and freed in case the operation fails.
+	/* Read/Read By Type/Read By Group Type may create a read object which
+	 * needs to be dequeued and freed in case the operation fails.
 	 */
-	if (pdu->request == 0x08 || pdu->request == 0x0a)
+	if (pdu->request == 0x08 || pdu->request == 0x0a ||
+					pdu->request == 0x10)
 		free(att_get_read(frame));
 }
 
@@ -384,10 +385,202 @@  static const struct bitfield_data chrc_prop_table[] = {
 	{ }
 };
 
+static void att_conn_data_free(void *data)
+{
+	struct att_conn_data *att_data = data;
+
+	gatt_db_unref(att_data->rdb);
+	gatt_db_unref(att_data->ldb);
+	queue_destroy(att_data->reads, free);
+	free(att_data);
+}
+
+static struct att_conn_data *att_get_conn_data(struct packet_conn_data *conn)
+{
+	struct att_conn_data *data;
+
+	if (!conn)
+		return NULL;
+
+	data = conn->data;
+
+	if (data)
+		return data;
+
+	data = new0(struct att_conn_data, 1);
+	data->rdb = gatt_db_new();
+	data->ldb = gatt_db_new();
+	conn->data = data;
+	conn->destroy = att_conn_data_free;
+
+	return data;
+}
+
+static void gatt_load_db(struct gatt_db *db, const char *filename,
+						struct timespec *mtim)
+{
+	struct stat st;
+
+	if (lstat(filename, &st))
+		return;
+
+	if (!gatt_db_isempty(db)) {
+		/* Check if file has been modified since last time */
+		if (st.st_mtim.tv_sec == mtim->tv_sec &&
+				    st.st_mtim.tv_nsec == mtim->tv_nsec)
+			return;
+		/* Clear db before reloading */
+		gatt_db_clear(db);
+	}
+
+	*mtim = st.st_mtim;
+
+	btd_settings_gatt_db_load(db, filename);
+}
+
+static void load_gatt_db(struct packet_conn_data *conn)
+{
+	struct att_conn_data *data = att_get_conn_data(conn);
+	char filename[PATH_MAX];
+	char local[18];
+	char peer[18];
+	uint8_t id[6], id_type;
+
+	ba2str((bdaddr_t *)conn->src, local);
+
+	if (keys_resolve_identity(conn->dst, id, &id_type))
+		ba2str((bdaddr_t *)id, peer);
+	else
+		ba2str((bdaddr_t *)conn->dst, peer);
+
+	create_filename(filename, PATH_MAX, "/%s/attributes", local);
+	gatt_load_db(data->ldb, filename, &data->ldb_mtim);
+
+	create_filename(filename, PATH_MAX, "/%s/cache/%s", local, peer);
+	gatt_load_db(data->rdb, filename, &data->rdb_mtim);
+}
+
+static struct gatt_db *get_db(const struct l2cap_frame *frame, bool rsp)
+{
+	struct packet_conn_data *conn;
+	struct att_conn_data *data;
+	struct gatt_db *db;
+
+	conn = packet_get_conn_data(frame->handle);
+	if (!conn)
+		return NULL;
+
+	/* Try loading local and remote gatt_db if not loaded yet */
+	load_gatt_db(conn);
+
+	data = conn->data;
+	if (!data)
+		return NULL;
+
+	if (frame->in) {
+		if (rsp)
+			db = data->rdb;
+		else
+			db = data->ldb;
+	} else {
+		if (rsp)
+			db = data->ldb;
+		else
+			db = data->rdb;
+	}
+
+	return db;
+}
+
+static struct gatt_db_attribute *insert_chrc(const struct l2cap_frame *frame,
+						uint16_t handle,
+						bt_uuid_t *uuid, uint8_t prop,
+						bool rsp)
+{
+	struct gatt_db *db;
+
+	db = get_db(frame, rsp);
+	if (!db)
+		return NULL;
+
+	return gatt_db_insert_characteristic(db, handle, uuid, 0, prop, NULL,
+							NULL, NULL);
+}
+
+static int bt_uuid_from_data(bt_uuid_t *uuid, const void *data, uint16_t size)
+{
+	uint128_t u128;
+
+	if (!uuid)
+		return -EINVAL;
+
+	switch (size) {
+	case 2:
+		return bt_uuid16_create(uuid, get_le16(data));
+	case 4:
+		return bt_uuid32_create(uuid, get_le32(data));
+	case 16:
+		memcpy(u128.data, data, sizeof(u128.data));
+		return bt_uuid128_create(uuid, u128);
+	}
+
+	return -EINVAL;
+}
+
+static bool svc_read(const struct l2cap_frame *frame, uint16_t *start,
+			uint16_t *end, bt_uuid_t *uuid)
+{
+	if (!l2cap_frame_get_le16((void *)frame, start))
+		return false;
+
+	if (!l2cap_frame_get_le16((void *)frame, end))
+		return false;
+
+	return !bt_uuid_from_data(uuid, frame->data, frame->size);
+}
+
+static struct gatt_db_attribute *insert_svc(const struct l2cap_frame *frame,
+						uint16_t handle,
+						bt_uuid_t *uuid, bool primary,
+						bool rsp, uint16_t num_handles)
+{
+	struct gatt_db *db;
+
+	db = get_db(frame, rsp);
+	if (!db)
+		return NULL;
+
+	return gatt_db_insert_service(db, handle, uuid, primary, num_handles);
+}
+
+static void pri_svc_read(const struct l2cap_frame *frame)
+{
+	uint16_t start, end;
+	bt_uuid_t uuid;
+
+	if (!svc_read(frame, &start, &end, &uuid))
+		return;
+
+	insert_svc(frame, start, &uuid, true, true, end - start + 1);
+}
+
+static void sec_svc_read(const struct l2cap_frame *frame)
+{
+	uint16_t start, end;
+	bt_uuid_t uuid;
+
+	if (!svc_read(frame, &start, &end, &uuid))
+		return;
+
+	insert_svc(frame, start, &uuid, true, false, end - start + 1);
+}
+
 static void print_chrc(const struct l2cap_frame *frame)
 {
 	uint8_t prop;
 	uint8_t mask;
+	uint16_t handle;
+	bt_uuid_t uuid;
 
 	if (!l2cap_frame_get_u8((void *)frame, &prop)) {
 		print_text(COLOR_ERROR, "Property: invalid size");
@@ -401,10 +594,16 @@  static void print_chrc(const struct l2cap_frame *frame)
 		print_text(COLOR_WHITE_BG, "    Unknown fields (0x%2.2x)",
 								mask);
 
-	if (!l2cap_frame_print_le16((void *)frame, "    Value Handle"))
+	if (!l2cap_frame_get_le16((void *)frame, &handle)) {
+		print_text(COLOR_ERROR, "    Value Handle: invalid size");
 		return;
+	}
 
+	print_field("    Value Handle: 0x%4.4x", handle);
 	print_uuid("    Value UUID", frame->data, frame->size);
+	bt_uuid_from_data(&uuid, frame->data, frame->size);
+
+	insert_chrc(frame, handle, &uuid, prop, true);
 }
 
 static void chrc_read(const struct l2cap_frame *frame)
@@ -2988,6 +3187,8 @@  struct gatt_handler {
 	void (*write)(const struct l2cap_frame *frame);
 	void (*notify)(const struct l2cap_frame *frame);
 } gatt_handlers[] = {
+	GATT_HANDLER(0x2800, pri_svc_read, NULL, NULL),
+	GATT_HANDLER(0x2801, sec_svc_read, NULL, NULL),
 	GATT_HANDLER(0x2803, chrc_read, NULL, NULL),
 	GATT_HANDLER(0x2902, ccc_read, ccc_write, NULL),
 	GATT_HANDLER(0x2bc4, ase_read, NULL, ase_notify),
@@ -3035,6 +3236,9 @@  static struct gatt_handler *get_handler_uuid(const bt_uuid_t *uuid)
 {
 	size_t i;
 
+	if (!uuid)
+		return NULL;
+
 	for (i = 0; i < ARRAY_SIZE(gatt_handlers); i++) {
 		struct gatt_handler *handler = &gatt_handlers[i];
 
@@ -3081,45 +3285,96 @@  static const char *att_format_str(uint8_t format)
 	}
 }
 
-static uint16_t print_info_data_16(const void *data, uint16_t len)
+static struct gatt_db_attribute *insert_desc(const struct l2cap_frame *frame,
+						uint16_t handle,
+						bt_uuid_t *uuid, bool rsp)
 {
-	while (len >= 4) {
-		print_field("Handle: 0x%4.4x", get_le16(data));
-		print_uuid("UUID", data + 2, 2);
-		data += 4;
-		len -= 4;
-	}
+	struct gatt_db *db;
 
-	return len;
+	db = get_db(frame, rsp);
+	if (!db)
+		return NULL;
+
+	return gatt_db_insert_descriptor(db, handle, uuid, 0, NULL, NULL, NULL);
 }
 
-static uint16_t print_info_data_128(const void *data, uint16_t len)
+static void att_find_info_rsp_16(const struct l2cap_frame *frame)
 {
-	while (len >= 18) {
-		print_field("Handle: 0x%4.4x", get_le16(data));
-		print_uuid("UUID", data + 2, 16);
-		data += 18;
-		len -= 18;
-	}
+	while (frame->size >= 4) {
+		uint16_t handle;
+		uint16_t u16;
+		bt_uuid_t uuid;
 
-	return len;
+		if (!l2cap_frame_get_le16((void *)frame, &handle)) {
+			print_text(COLOR_ERROR, "    Handle: invalid size");
+			return;
+		}
+
+		if (!l2cap_frame_get_le16((void *)frame, &u16)) {
+			print_text(COLOR_ERROR, "    UUID: invalid size");
+			return;
+		}
+
+		print_field("Handle: 0x%4.4x", handle);
+		print_uuid("UUID", &u16, 2);
+
+		bt_uuid16_create(&uuid, u16);
+
+		insert_desc(frame, handle, &uuid, true);
+	}
+}
+
+static void att_find_info_rsp_128(const struct l2cap_frame *frame)
+{
+	while (frame->size >= 18) {
+		uint16_t handle;
+		bt_uuid_t uuid;
+
+		if (!l2cap_frame_get_le16((void *)frame, &handle)) {
+			print_text(COLOR_ERROR, "    Handle: invalid size");
+			return;
+		}
+
+		if (frame->size < 16) {
+			print_text(COLOR_ERROR, "    UUID: invalid size");
+			return;
+		}
+
+		print_field("Handle: 0x%4.4x", handle);
+		print_uuid("UUID", frame->data, 16);
+
+		bt_uuid_from_data(&uuid, frame->data, 16);
+
+		if (!l2cap_frame_pull((void *)frame, frame, 16))
+			return;
+
+		insert_desc(frame, handle, &uuid, true);
+	}
 }
 
 static void att_find_info_rsp(const struct l2cap_frame *frame)
 {
-	const uint8_t *format = frame->data;
-	uint16_t len;
+	uint8_t format;
 
-	print_field("Format: %s (0x%2.2x)", att_format_str(*format), *format);
+	if (!l2cap_frame_get_u8((void *)frame, &format)) {
+		print_text(COLOR_ERROR, "    Format: invalid size");
+		goto done;
+	}
 
-	if (*format == 0x01)
-		len = print_info_data_16(frame->data + 1, frame->size - 1);
-	else if (*format == 0x02)
-		len = print_info_data_128(frame->data + 1, frame->size - 1);
-	else
-		len = frame->size - 1;
+	print_field("Format: %s (0x%2.2x)", att_format_str(format), format);
 
-	packet_hexdump(frame->data + (frame->size - len), len);
+	switch (format) {
+	case 0x01:
+		att_find_info_rsp_16(frame);
+		break;
+	case 0x02:
+		att_find_info_rsp_128(frame);
+		break;
+	}
+
+done:
+	if (frame->size)
+		packet_hexdump(frame->data, frame->size);
 }
 
 static void att_find_by_type_val_req(const struct l2cap_frame *frame)
@@ -3146,69 +3401,34 @@  static void att_find_by_type_val_rsp(const struct l2cap_frame *frame)
 	packet_hexdump(ptr, len);
 }
 
-static int bt_uuid_from_data(bt_uuid_t *uuid, const void *data, uint16_t size)
+static struct gatt_db_attribute *get_attribute(const struct l2cap_frame *frame,
+						uint16_t handle, bool rsp)
 {
-	uint128_t u128;
+	struct gatt_db *db;
 
-	switch (size) {
-	case 2:
-		return bt_uuid16_create(uuid, get_le16(data));
-	case 4:
-		return bt_uuid32_create(uuid, get_le32(data));
-	case 16:
-		memcpy(u128.data, data, sizeof(u128.data));
-		return bt_uuid128_create(uuid, u128);
-	}
-
-	return -EINVAL;
-}
-
-static void att_conn_data_free(void *data)
-{
-	struct att_conn_data *att_data = data;
-
-	gatt_db_unref(att_data->rdb);
-	gatt_db_unref(att_data->ldb);
-	queue_destroy(att_data->reads, free);
-	free(att_data);
-}
-
-static struct att_conn_data *att_get_conn_data(struct packet_conn_data *conn)
-{
-	struct att_conn_data *data;
-
-	if (!conn)
+	db = get_db(frame, rsp);
+	if (!db)
 		return NULL;
 
-	data = conn->data;
-
-	if (data)
-		return data;
-
-	data = new0(struct att_conn_data, 1);
-	data->rdb = gatt_db_new();
-	data->ldb = gatt_db_new();
-	conn->data = data;
-	conn->destroy = att_conn_data_free;
-
-	return data;
+	return gatt_db_get_attribute(db, handle);
 }
 
-static void att_read_type_req(const struct l2cap_frame *frame)
+static void queue_read(const struct l2cap_frame *frame, bt_uuid_t *uuid,
+					uint16_t handle)
 {
-	bt_uuid_t uuid;
 	struct packet_conn_data *conn;
 	struct att_conn_data *data;
 	struct att_read *read;
+	struct gatt_db_attribute *attr = NULL;
 	struct gatt_handler *handler;
 
-	print_handle_range("Handle range", frame->data);
-	print_uuid("Attribute type", frame->data + 4, frame->size - 4);
+	if (handle) {
+		attr = get_attribute(frame, handle, false);
+		if (!attr)
+			return;
+	}
 
-	if (bt_uuid_from_data(&uuid, frame->data + 4, frame->size - 4))
-		return;
-
-	handler = get_handler_uuid(&uuid);
+	handler = attr ? get_handler(attr) : get_handler_uuid(uuid);
 	if (!handler || !handler->read)
 		return;
 
@@ -3221,6 +3441,7 @@  static void att_read_type_req(const struct l2cap_frame *frame)
 		data->reads = queue_new();
 
 	read = new0(struct att_read, 1);
+	read->attr = attr;
 	read->in = frame->in;
 	read->chan = frame->chan;
 	read->func = handler->read;
@@ -3228,6 +3449,19 @@  static void att_read_type_req(const struct l2cap_frame *frame)
 	queue_push_tail(data->reads, read);
 }
 
+static void att_read_type_req(const struct l2cap_frame *frame)
+{
+	bt_uuid_t uuid;
+
+	print_handle_range("Handle range", frame->data);
+	print_uuid("Attribute type", frame->data + 4, frame->size - 4);
+
+	if (bt_uuid_from_data(&uuid, frame->data + 4, frame->size - 4))
+		return;
+
+	queue_read(frame, &uuid, 0x0000);
+}
+
 static void att_read_type_rsp(const struct l2cap_frame *frame)
 {
 	uint8_t len;
@@ -3241,83 +3475,6 @@  static void att_read_type_rsp(const struct l2cap_frame *frame)
 	print_data_list("Attribute data list", len, frame);
 }
 
-static void gatt_load_db(struct gatt_db *db, const char *filename,
-						struct timespec *mtim)
-{
-	struct stat st;
-
-	if (lstat(filename, &st))
-		return;
-
-	if (!gatt_db_isempty(db)) {
-		/* Check if file has been modified since last time */
-		if (st.st_mtim.tv_sec == mtim->tv_sec &&
-				    st.st_mtim.tv_nsec == mtim->tv_nsec)
-			return;
-		/* Clear db before reloading */
-		gatt_db_clear(db);
-	}
-
-	*mtim = st.st_mtim;
-
-	btd_settings_gatt_db_load(db, filename);
-}
-
-static void load_gatt_db(struct packet_conn_data *conn)
-{
-	struct att_conn_data *data = att_get_conn_data(conn);
-	char filename[PATH_MAX];
-	char local[18];
-	char peer[18];
-	uint8_t id[6], id_type;
-
-	ba2str((bdaddr_t *)conn->src, local);
-
-	if (keys_resolve_identity(conn->dst, id, &id_type))
-		ba2str((bdaddr_t *)id, peer);
-	else
-		ba2str((bdaddr_t *)conn->dst, peer);
-
-	create_filename(filename, PATH_MAX, "/%s/attributes", local);
-	gatt_load_db(data->ldb, filename, &data->ldb_mtim);
-
-	create_filename(filename, PATH_MAX, "/%s/cache/%s", local, peer);
-	gatt_load_db(data->rdb, filename, &data->rdb_mtim);
-}
-
-static struct gatt_db_attribute *get_attribute(const struct l2cap_frame *frame,
-						uint16_t handle, bool rsp)
-{
-	struct packet_conn_data *conn;
-	struct att_conn_data *data;
-	struct gatt_db *db;
-
-	conn = packet_get_conn_data(frame->handle);
-	if (!conn)
-		return NULL;
-
-	/* Try loading local and remote gatt_db if not loaded yet */
-	load_gatt_db(conn);
-
-	data = conn->data;
-	if (!data)
-		return NULL;
-
-	if (frame->in) {
-		if (rsp)
-			db = data->rdb;
-		else
-			db = data->ldb;
-	} else {
-		if (rsp)
-			db = data->ldb;
-		else
-			db = data->rdb;
-	}
-
-	return gatt_db_get_attribute(db, handle);
-}
-
 static void print_handle(const struct l2cap_frame *frame, uint16_t handle,
 								bool rsp)
 {
@@ -3336,38 +3493,13 @@  static void att_read_req(const struct l2cap_frame *frame)
 {
 	const struct bt_l2cap_att_read_req *pdu = frame->data;
 	uint16_t handle;
-	struct packet_conn_data *conn;
-	struct att_conn_data *data;
-	struct att_read *read;
-	struct gatt_db_attribute *attr;
-	struct gatt_handler *handler;
 
 	l2cap_frame_pull((void *)frame, frame, sizeof(*pdu));
 
 	handle = le16_to_cpu(pdu->handle);
 	print_handle(frame, handle, false);
 
-	attr = get_attribute(frame, handle, false);
-	if (!attr)
-		return;
-
-	handler = get_handler(attr);
-	if (!handler || !handler->read)
-		return;
-
-	conn = packet_get_conn_data(frame->handle);
-	data = conn->data;
-
-	if (!data->reads)
-		data->reads = queue_new();
-
-	read = new0(struct att_read, 1);
-	read->attr = attr;
-	read->in = frame->in;
-	read->chan = frame->chan;
-	read->func = handler->read;
-
-	queue_push_tail(data->reads, read);
+	queue_read(frame, NULL, handle);
 }
 
 static void att_read_rsp(const struct l2cap_frame *frame)
@@ -3409,40 +3541,60 @@  static void att_read_multiple_req(const struct l2cap_frame *frame)
 
 static void att_read_group_type_req(const struct l2cap_frame *frame)
 {
+	bt_uuid_t uuid;
+
 	print_handle_range("Handle range", frame->data);
 	print_uuid("Attribute group type", frame->data + 4, frame->size - 4);
+
+	if (bt_uuid_from_data(&uuid, frame->data + 4, frame->size - 4))
+		return;
+
+	queue_read(frame, &uuid, 0x0000);
 }
 
 static void print_group_list(const char *label, uint8_t length,
-					const void *data, uint16_t size)
+					const struct l2cap_frame *frame)
 {
+	struct att_read *read;
 	uint8_t count;
 
 	if (length == 0)
 		return;
 
-	count = size / length;
+	read = att_get_read(frame);
+
+	count = frame->size / length;
 
 	print_field("%s: %u entr%s", label, count, count == 1 ? "y" : "ies");
 
-	while (size >= length) {
-		print_handle_range("Handle range", data);
-		print_uuid("UUID", data + 4, length - 4);
+	while (frame->size >= length) {
+		print_handle_range("Handle range", frame->data);
+		print_uuid("UUID", frame->data + 4, length - 4);
 
-		data += length;
-		size -= length;
+		if (read) {
+			struct l2cap_frame f;
+
+			l2cap_frame_clone_size(&f, frame, length);
+
+			read->func(&f);
+		}
+
+		if (!l2cap_frame_pull((void *)frame, frame, length))
+			break;
 	}
 
-	packet_hexdump(data, size);
+	packet_hexdump(frame->data, frame->size);
+	free(read);
 }
 
 static void att_read_group_type_rsp(const struct l2cap_frame *frame)
 {
 	const struct bt_l2cap_att_read_group_type_rsp *pdu = frame->data;
 
+	l2cap_frame_pull((void *)frame, frame, sizeof(*pdu));
+
 	print_field("Attribute data length: %d", pdu->length);
-	print_group_list("Attribute group list", pdu->length,
-					frame->data + 1, frame->size - 1);
+	print_group_list("Attribute group list", pdu->length, frame);
 }
 
 static void print_write(const struct l2cap_frame *frame, uint16_t handle,