[PATCH BlueZ v4 07/10] core: device: Make profile calls in GATT events

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch correctly integrates all profile calls into the various GATT
client events (ready, service-added, service-removed) so that profiles
can perform the necessary updates. The major changes introduced in this
this patch are:

  1. Added new profile probe/remove functions for GATT services, which
     operate on a btd_device's client db and initialize btd_service
     instances with start & end handles:
        - device_probe_gatt_profiles
        - device_probe_gatt_profile
        - device_remove_gatt_profile
        - device_accept_gatt_profiles

  2. device_probe_gatt_profiles is called when the gatt-client becomes
     ready. Profiles are then notified with the "accept" call.

  3. device_probe_gatt_profile is called when a new GATT service is
     added to the db.

  4. device_remove_gatt_profile is called when a GATT service is removed
     from the db.

  5. Profiles are notified that the gatt-client is ready via the
     btd_service "accept" function on a per-service basis. Currently this
     is always called immediately after a probe.
---
 src/device.c | 261 ++++++++++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 212 insertions(+), 49 deletions(-)

diff --git a/src/device.c b/src/device.c
index 854d9f3..6242c76 100644
--- a/src/device.c
+++ b/src/device.c
@@ -292,6 +292,27 @@ static GSList *find_service_with_state(GSList *list,
 	return NULL;
 }
 
+static GSList *find_service_with_gatt_handles(GSList *list,
+							uint16_t start_handle,
+							uint16_t end_handle)
+{
+	GSList *l;
+	uint16_t svc_start, svc_end;
+
+	for (l = list; l != NULL; l = g_slist_next(l)) {
+		struct btd_service *service = l->data;
+
+		if (!btd_service_get_gatt_handles(service, &svc_start,
+								&svc_end))
+			continue;
+
+		if (svc_start == start_handle && svc_end == end_handle)
+			return l;
+	}
+
+	return NULL;
+}
+
 static void update_technologies(GKeyFile *file, struct btd_device *dev)
 {
 	const char *list[2];
@@ -2390,15 +2411,168 @@ static void add_primary(struct gatt_db_attribute *attr, void *user_data)
 	*new_services = g_slist_append(*new_services, prim);
 }
 
+static void device_add_uuids(struct btd_device *device, GSList *uuids)
+{
+	GSList *l;
+
+	for (l = uuids; l != NULL; l = g_slist_next(l)) {
+		GSList *match = g_slist_find_custom(device->uuids, l->data,
+							bt_uuid_strcmp);
+		if (match)
+			continue;
+
+		device->uuids = g_slist_insert_sorted(device->uuids,
+						g_strdup(l->data),
+						bt_uuid_strcmp);
+	}
+
+	g_dbus_emit_property_changed(dbus_conn, device->path,
+						DEVICE_INTERFACE, "UUIDs");
+}
+
 static void store_services(struct btd_device *device);
 
+struct gatt_probe_data {
+	struct btd_device *dev;
+	bool add_uuid_to_device;
+	GSList *uuids;
+	struct gatt_db_attribute *cur_attr;
+	char cur_uuid[MAX_LEN_UUID_STR];
+	uint16_t start_handle, end_handle;
+};
+
+static bool device_match_profile(struct btd_device *device,
+					struct btd_profile *profile,
+					GSList *uuids)
+{
+	if (profile->remote_uuid == NULL)
+		return false;
+
+	if (g_slist_find_custom(uuids, profile->remote_uuid,
+							bt_uuid_strcmp) == NULL)
+		return false;
+
+	return true;
+}
+
+static void dev_probe_gatt(struct btd_profile *p, void *user_data)
+{
+	struct gatt_probe_data *data = user_data;
+	struct btd_service *service;
+
+	if (p->device_probe == NULL)
+		return;
+
+	if (!device_match_profile(data->dev, p, g_slist_last(data->uuids)))
+		return;
+
+	service = service_create_gatt(data->dev, p, data->start_handle,
+							data->end_handle);
+	if (!service)
+		return;
+
+	if (service_probe(service) < 0) {
+		btd_service_unref(service);
+		return;
+	}
+
+	data->dev->services = g_slist_append(data->dev->services, service);
+}
+
+static void dev_probe_gatt_profile(struct gatt_db_attribute *attr,
+							void *user_data)
+{
+	struct gatt_probe_data *data = user_data;
+	bt_uuid_t uuid;
+	GSList *l;
+
+	gatt_db_attribute_get_service_data(attr, &data->start_handle,
+							&data->end_handle, NULL,
+							&uuid);
+	bt_uuid_to_string(&uuid, data->cur_uuid, sizeof(data->cur_uuid));
+
+	data->uuids = g_slist_append(data->uuids, g_strdup(data->cur_uuid));
+
+	data->cur_attr = attr;
+
+	btd_profile_foreach(dev_probe_gatt, data);
+
+	if (!data->add_uuid_to_device)
+		return;
+
+	l = g_slist_append(l, g_strdup(data->cur_uuid));
+	device_add_uuids(data->dev, l);
+}
+
+static void device_probe_gatt_profile(struct btd_device *device,
+						struct gatt_db_attribute *attr)
+{
+	struct gatt_probe_data data;
+
+	memset(&data, 0, sizeof(data));
+
+	data.dev = device;
+	data.add_uuid_to_device = true;
+
+	dev_probe_gatt_profile(attr, &data);
+	g_slist_free_full(data.uuids, g_free);
+}
+
+static void device_probe_gatt_profiles(struct btd_device *device)
+{
+	struct gatt_probe_data data;
+	char addr[18];
+
+	ba2str(&device->bdaddr, addr);
+
+	if (device->blocked) {
+		DBG("Skipping profiles for blocked device %s", addr);
+		return;
+	}
+
+	memset(&data, 0, sizeof(data));
+
+	data.dev = device;
+
+	gatt_db_foreach_service(device->db, NULL, dev_probe_gatt_profile,
+									&data);
+	device_add_uuids(device, data.uuids);
+	g_slist_free_full(data.uuids, g_free);
+}
+
+static void device_accept_gatt_profiles(struct btd_device *device)
+{
+	GSList *l;
+
+	for (l = device->services; l != NULL; l = g_slist_next(l))
+		service_accept(l->data);
+}
+
+static void device_remove_gatt_profile(struct btd_device *device,
+						struct gatt_db_attribute *attr)
+{
+	uint16_t start, end;
+	struct btd_service *service;
+	GSList *l;
+
+	gatt_db_attribute_get_service_handles(attr, &start, &end);
+
+	l = find_service_with_gatt_handles(device->services, start, end);
+	if (!l)
+		return;
+
+	service = l->data;
+	device->services = g_slist_delete_link(device->services, l);
+	device->pending = g_slist_remove(device->pending, service);
+	service_remove(service);
+}
+
 static void gatt_service_added(struct gatt_db_attribute *attr, void *user_data)
 {
 	struct btd_device *device = user_data;
-	struct gatt_primary *prim;
 	GSList *new_service = NULL;
-	GSList *profiles_added = NULL;
 	uint16_t start, end;
+	GSList *l;
 
 	if (!bt_gatt_client_is_ready(device->client))
 		return;
@@ -2417,14 +2591,13 @@ static void gatt_service_added(struct gatt_db_attribute *attr, void *user_data)
 
 	device_register_primaries(device, new_service, -1);
 
-	prim = new_service->data;
-	profiles_added = g_slist_append(profiles_added, g_strdup(prim->uuid));
+	device_probe_gatt_profile(device, attr);
 
-	device_probe_profiles(device, profiles_added);
+	l = find_service_with_gatt_handles(device->services, start, end);
+	if (l)
+		service_accept(l->data);
 
 	store_services(device);
-
-	g_slist_free_full(profiles_added, g_free);
 }
 
 static gint prim_attr_cmp(gconstpointer a, gconstpointer b)
@@ -2438,6 +2611,14 @@ static gint prim_attr_cmp(gconstpointer a, gconstpointer b)
 	return !(prim->range.start == start && prim->range.end == end);
 }
 
+static gint prim_uuid_cmp(gconstpointer a, gconstpointer b)
+{
+	const struct gatt_primary *prim = a;
+	const char *uuid = b;
+
+	return bt_uuid_strcmp(prim->uuid, uuid);
+}
+
 static void gatt_service_removed(struct gatt_db_attribute *attr,
 								void *user_data)
 {
@@ -2461,20 +2642,24 @@ static void gatt_service_removed(struct gatt_db_attribute *attr,
 	prim = l->data;
 	device->primaries = g_slist_delete_link(device->primaries, l);
 
-	/* Remove the corresponding UUIDs entry */
-	l = g_slist_find_custom(device->uuids, prim->uuid, bt_uuid_strcmp);
-	device->uuids = g_slist_delete_link(device->uuids, l);
-	g_free(prim);
-
-	store_services(device);
+	device_remove_gatt_profile(device, attr);
 
 	/*
-	 * TODO: Notify the profiles somehow. It may be sufficient for each
-	 * profile to register a service_removed handler.
+	 * Remove the corresponding UUIDs entry, only if this is the last
+	 * service with this UUID.
 	 */
+	l = g_slist_find_custom(device->uuids, prim->uuid, bt_uuid_strcmp);
 
-	g_dbus_emit_property_changed(dbus_conn, device->path,
+	if (!g_slist_find_custom(device->primaries, prim->uuid,
+							prim_uuid_cmp)) {
+		device->uuids = g_slist_delete_link(device->uuids, l);
+		g_dbus_emit_property_changed(dbus_conn, device->path,
 						DEVICE_INTERFACE, "UUIDs");
+	}
+
+	g_free(prim);
+
+	store_services(device);
 }
 
 static struct btd_device *device_new(struct btd_adapter *adapter,
@@ -2975,20 +3160,6 @@ GSList *btd_device_get_uuids(struct btd_device *device)
 	return device->uuids;
 }
 
-static bool device_match_profile(struct btd_device *device,
-					struct btd_profile *profile,
-					GSList *uuids)
-{
-	if (profile->remote_uuid == NULL)
-		return false;
-
-	if (g_slist_find_custom(uuids, profile->remote_uuid,
-							bt_uuid_strcmp) == NULL)
-		return false;
-
-	return true;
-}
-
 struct probe_data {
 	struct btd_device *dev;
 	GSList *uuids;
@@ -3065,7 +3236,6 @@ void device_remove_profile(gpointer a, gpointer b)
 void device_probe_profiles(struct btd_device *device, GSList *uuids)
 {
 	struct probe_data d = { device, uuids };
-	GSList *l;
 	char addr[18];
 
 	ba2str(&device->bdaddr, addr);
@@ -3080,19 +3250,7 @@ void device_probe_profiles(struct btd_device *device, GSList *uuids)
 	btd_profile_foreach(dev_probe, &d);
 
 add_uuids:
-	for (l = uuids; l != NULL; l = g_slist_next(l)) {
-		GSList *match = g_slist_find_custom(device->uuids, l->data,
-							bt_uuid_strcmp);
-		if (match)
-			continue;
-
-		device->uuids = g_slist_insert_sorted(device->uuids,
-						g_strdup(l->data),
-						bt_uuid_strcmp);
-	}
-
-	g_dbus_emit_property_changed(dbus_conn, device->path,
-						DEVICE_INTERFACE, "UUIDs");
+	device_add_uuids(device, uuids);
 }
 
 static void store_sdp_record(GKeyFile *key_file, sdp_record_t *rec)
@@ -3412,6 +3570,13 @@ static void search_cb(sdp_list_t *recs, int err, gpointer user_data)
 	if (primaries)
 		device_register_primaries(device, primaries, ATT_PSM);
 
+	/*
+	 * TODO: The btd_service instances for GATT services need to be
+	 * initialized with the service handles. Eventually this code should
+	 * perform ATT protocol service discovery over the ATT PSM to obtain
+	 * the full list of services and populate a client-role gatt_db over
+	 * BR/EDR.
+	 */
 	device_probe_profiles(device, req->profiles_added);
 
 	/* Propagate services changes */
@@ -3640,7 +3805,7 @@ static void register_gatt_services(struct browse_req *req)
 
 	device_register_primaries(device, services, -1);
 
-	device_probe_profiles(device, req->profiles_added);
+	device_probe_gatt_profiles(device);
 
 	device_svc_resolved(device, device->bdaddr_type, 0);
 
@@ -3675,10 +3840,8 @@ static void gatt_client_ready_cb(bool success, uint8_t att_ecode,
 	if (device->browse)
 		register_gatt_services(device->browse);
 
-	/*
-	 * TODO: Change attio callbacks to accept a gatt-client instead of a
-	 * GAttrib.
-	 */
+	device_accept_gatt_profiles(device);
+
 	g_slist_foreach(device->attios, attio_connected, device->attrib);
 }
 
-- 
2.2.0.rc0.207.ga3a616c

--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux