[PATCH] Notify to device drivers when the SDP records change

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

 



When the remote SDP records that make a driver to be loaded change
(added, remove, or updated) And the driver is not going to be
removed, a notification is sent to the device driver. This
notification is optional annnd does not fail  if the driver does
not implemment it.
---
 src/device.c |  179 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/device.h |    1 +
 2 files changed, 178 insertions(+), 2 deletions(-)

diff --git a/src/device.c b/src/device.c
index 6af76d1..b8d5e26 100644
--- a/src/device.c
+++ b/src/device.c
@@ -99,7 +99,11 @@ struct browse_req {
 	GSList *match_uuids;
 	GSList *profiles_added;
 	GSList *profiles_removed;
+	GSList *profiles_updated;
 	sdp_list_t *records;
+	sdp_list_t *records_added;
+	sdp_list_t *records_removed;
+	sdp_list_t *records_changed;
 	int search_uuid;
 	int reconnect_attempt;
 	guint listener_id;
@@ -1328,6 +1332,30 @@ static void device_remove_drivers(struct btd_device *device, GSList *uuids)
 		sdp_list_free(records, (sdp_free_func_t) sdp_record_free);
 }
 
+static void device_update_drivers(struct btd_device *device, GSList *profiles)
+{
+	GSList *l;
+
+	DBG("Update drivers for %s", device->path);
+
+	for (l = device->drivers; l; l = l->next) {
+		struct btd_driver_data *driver_data = l->data;
+		struct btd_device_driver *driver = driver_data->driver;
+		GSList *updated_uuids;
+
+		DBG("Updating device %s", device->path);
+
+		updated_uuids = device_match_driver(device, driver, profiles);
+
+		if (!updated_uuids)
+			continue;
+
+		DBG("No updated uuids");
+		if (driver->update)
+			driver->update(device, updated_uuids);
+	}
+}
+
 static void services_changed(struct btd_device *device)
 {
 	DBusConnection *conn = get_dbus_connection();
@@ -1353,11 +1381,50 @@ static int rec_cmp(const void *a, const void *b)
 	return r1->handle - r2->handle;
 }
 
+static void check_changes(struct browse_req *req, sdp_list_t *stored_recs,
+							sdp_record_t *rec)
+{
+	sdp_list_t *prev_rec, *remove;
+	sdp_buf_t new_buf, prev_buf;
+
+	prev_rec = sdp_list_find(stored_recs, rec, rec_cmp);
+	if (!prev_rec) {
+		req->records_added = sdp_list_append(req->records_added,
+							sdp_copy_record(rec));
+		return;
+	} else if ((remove = sdp_list_find(req->records_removed, rec, rec_cmp))){
+		req->records_removed = sdp_list_remove(req->records_removed,
+								remove->data);
+		sdp_record_free(remove->data);
+	}
+
+	if (sdp_gen_record_pdu(rec, &new_buf) < 0)
+		return;
+	if (sdp_gen_record_pdu(prev_rec->data, &prev_buf) < 0) {
+		free(new_buf.data);
+		return;
+	}
+
+	if (new_buf.data_size != prev_buf.data_size)
+		goto change;
+
+	if (memcmp(new_buf.data, prev_buf.data, new_buf.data_size) == 0)
+		goto end;
+
+change:
+	req->records_changed = sdp_list_append(req->records_changed,
+							sdp_copy_record(rec));
+
+end:
+	free(new_buf.data);
+	free(prev_buf.data);
+}
+
 static void update_services(struct browse_req *req, sdp_list_t *recs)
 {
 	struct btd_device *device = req->device;
 	struct btd_adapter *adapter = device_get_adapter(device);
-	sdp_list_t *seq;
+	sdp_list_t *seq, *stored_recs;
 	char srcaddr[18], dstaddr[18];
 	bdaddr_t src;
 
@@ -1365,6 +1432,8 @@ static void update_services(struct browse_req *req, sdp_list_t *recs)
 	ba2str(&src, srcaddr);
 	ba2str(&device->bdaddr, dstaddr);
 
+	stored_recs = read_records(&src, &req->device->bdaddr);
+
 	for (seq = recs; seq; seq = seq->next) {
 		sdp_record_t *rec = (sdp_record_t *) seq->data;
 		sdp_list_t *svcclass = NULL;
@@ -1418,6 +1487,8 @@ static void update_services(struct browse_req *req, sdp_list_t *recs)
 			continue;
 		}
 
+		check_changes(req, stored_recs, rec);
+
 		store_record(srcaddr, dstaddr, rec);
 
 		/* Copy record */
@@ -1439,6 +1510,8 @@ static void update_services(struct browse_req *req, sdp_list_t *recs)
 
 		sdp_list_free(svcclass, free);
 	}
+
+	sdp_list_free(stored_recs, (sdp_free_func_t)sdp_record_free);
 }
 
 static void store_profiles(struct btd_device *device)
@@ -1473,6 +1546,97 @@ static void create_device_reply(struct btd_device *device, struct browse_req *re
 	g_dbus_send_message(req->conn, reply);
 }
 
+static void remove_record(void *a, void *b)
+{
+	sdp_record_t *rec = a;
+	struct btd_device *device = b;
+	char srcaddr[18], dstaddr[18];
+	bdaddr_t src;
+
+	adapter_get_address(device_get_adapter(device), &src);
+	ba2str(&src, srcaddr);
+	ba2str(&device->bdaddr, dstaddr);
+
+	delete_record(srcaddr, dstaddr, rec->handle);
+}
+
+static void extract_updated_uuids(struct browse_req *req, sdp_record_t *rec)
+{
+	sdp_list_t *svcclass = NULL;
+	gchar *profile_uuid;
+	GSList *l;
+
+	if (sdp_get_service_classes(rec, &svcclass) < 0)
+			return;
+
+	/* Check for empty service classes list */
+	if (svcclass == NULL) {
+		DBG("Skipping record with no service classes");
+		return;
+	}
+
+	/* Extract the first element and skip the remainning */
+	profile_uuid = bt_uuid2string(svcclass->data);
+	if (!profile_uuid) {
+		sdp_list_free(svcclass, free);
+		return;
+	}
+
+	l = g_slist_find_custom(req->profiles_added, profile_uuid,
+						(GCompareFunc) strcmp);
+	if (l)
+		goto end;
+
+	l = g_slist_find_custom(req->profiles_removed, profile_uuid,
+						(GCompareFunc) strcmp);
+	if (l)
+		goto end;
+
+	l = g_slist_find_custom(req->profiles_updated, profile_uuid,
+						(GCompareFunc) strcmp);
+	if (l)
+		goto end;
+
+	req->profiles_updated = g_slist_append(req->profiles_updated,
+							profile_uuid);
+
+end:
+	g_free(profile_uuid);
+	sdp_list_free(svcclass, free);
+}
+
+static void get_updated_uuids(struct browse_req *req)
+{
+	sdp_list_t *l;
+
+	for (l = req->records_added; l; l = l->next) {
+		extract_updated_uuids(req, l->data);
+	}
+
+	for (l = req->records_removed; l; l = l->next) {
+		extract_updated_uuids(req, l->data);
+	}
+
+	for (l = req->records_changed; l; l = l->next) {
+		extract_updated_uuids(req, l->data);
+	}
+
+	if (req->records_added)
+		sdp_list_free(req->records_added,
+					(sdp_free_func_t)sdp_record_free);
+
+	if (req->records_changed)
+		sdp_list_free(req->records_changed,
+					(sdp_free_func_t)sdp_record_free);
+
+	if (req->records_removed) {
+		sdp_list_foreach(req->records_removed, remove_record,
+								req->device);
+		sdp_list_free(req->records_removed,
+					(sdp_free_func_t)sdp_record_free);
+	}
+}
+
 static void search_cb(sdp_list_t *recs, int err, gpointer user_data)
 {
 	struct browse_req *req = user_data;
@@ -1493,7 +1657,10 @@ static void search_cb(sdp_list_t *recs, int err, gpointer user_data)
 	device->tmp_records = req->records;
 	req->records = NULL;
 
-	if (!req->profiles_added && !req->profiles_removed) {
+	get_updated_uuids(req);
+
+	if (!req->profiles_added && !req->profiles_removed &&
+						!req->profiles_updated) {
 		DBG("%s: No service update", device->path);
 		goto send_reply;
 	}
@@ -1506,6 +1673,10 @@ static void search_cb(sdp_list_t *recs, int err, gpointer user_data)
 	if (req->profiles_removed)
 		device_remove_drivers(device, req->profiles_removed);
 
+	/* Notify the UUIDS whoose SDP records have changed */
+	if (req->profiles_updated)
+		device_update_drivers(device, req->profiles_updated);
+
 	/* Propagate services changes */
 	services_changed(req->device);
 
@@ -1574,6 +1745,7 @@ done:
 static void init_browse(struct browse_req *req, gboolean reverse)
 {
 	GSList *l;
+	bdaddr_t src;
 
 	/* If we are doing reverse-SDP don't try to detect removed profiles
 	 * since some devices hide their service records while they are
@@ -1585,6 +1757,9 @@ static void init_browse(struct browse_req *req, gboolean reverse)
 	for (l = req->device->uuids; l; l = l->next)
 		req->profiles_removed = g_slist_append(req->profiles_removed,
 						l->data);
+
+	adapter_get_address(device_get_adapter(req->device), &src);
+	req->records_removed = read_records(&src, &req->device->bdaddr);
 }
 
 int device_browse(struct btd_device *device, DBusConnection *conn,
diff --git a/src/device.h b/src/device.h
index 5f75e61..72593c4 100644
--- a/src/device.h
+++ b/src/device.h
@@ -103,6 +103,7 @@ struct btd_device_driver {
 	const char **uuids;
 	int (*probe) (struct btd_device *device, GSList *uuids);
 	void (*remove) (struct btd_device *device);
+	void (*update) (struct btd_device *device, GSList *uuids);
 };
 
 int btd_register_device_driver(struct btd_device_driver *driver);
-- 
1.7.1

--
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