[PATCH v3 2/2] input: Expose HIDNormallyConnectable and HIDReconnectInitiate properties

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

 



The "Connectability" of a HID device, as defined by the HID spec,
governs the way the host may and should connect to a HID device or
expect a connection from it. In the comon case of mice and keyboards
the HIDNormallyConnectable is FALSE and HIDReconnectInitiate is TRUE,
since those devices only attempt a connection to the host when they
have some data to transfer. A connection attempt initiated from the
host after the device drops the connection (while still paired) will
result in a Page timeout.

This patch exposes these two HID properties in a new Input1 interface,
updates them every time new SDP records are available and persists them
in a new device "input" file.
---
 profiles/input/device.c | 170 +++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 168 insertions(+), 2 deletions(-)

diff --git a/profiles/input/device.c b/profiles/input/device.c
index 1da9d99..a2a87a0 100644
--- a/profiles/input/device.c
+++ b/profiles/input/device.c
@@ -54,6 +54,8 @@
 
 #include "sdp-client.h"
 
+#define INPUT_INTERFACE "org.bluez.Input1"
+
 struct input_device {
 	struct btd_device	*device;
 	char			*path;
@@ -69,8 +71,10 @@ struct input_device {
 	int			timeout;
 	struct hidp_connadd_req *req;
 	guint			dc_id;
-	gboolean		disable_sdp;
+	bool			disable_sdp;
 	char			*name;
+	bool			normally_connectable;
+	bool			reconnect_initiate;
 };
 
 static GSList *devices = NULL;
@@ -116,6 +120,81 @@ static void input_device_free(struct input_device *idev)
 	g_free(idev);
 }
 
+static void input_device_config_filename(struct input_device *idev,
+						char *filename, size_t maxlen)
+{
+	char adapter_addr[18];
+	char device_addr[18];
+
+	ba2str(device_get_address(idev->device), device_addr);
+	ba2str(adapter_get_address(device_get_adapter(idev->device)),
+								adapter_addr);
+	snprintf(filename, maxlen, STORAGEDIR "/%s/%s/input", adapter_addr,
+								device_addr);
+	filename[maxlen-1] = '\0';
+}
+
+static void input_device_load_props(struct input_device *idev)
+{
+	char filename[PATH_MAX + 1];
+	GKeyFile *key_file;
+	GError *err = NULL;
+
+	input_device_config_filename(idev, filename, PATH_MAX + 1);
+
+	key_file = g_key_file_new();
+	if (!key_file)
+		return;
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+	/* Load HID connectivity properties */
+	idev->normally_connectable = g_key_file_get_boolean(key_file,
+				"Input", "NormallyConnectable", &err);
+	if (err) {
+		idev->normally_connectable = FALSE;
+		g_error_free(err);
+		err = NULL;
+	}
+
+	idev->reconnect_initiate = g_key_file_get_boolean(key_file,
+				"Input", "ReconnectInitiate", &err);
+	if (err) {
+		idev->reconnect_initiate = TRUE;
+		g_error_free(err);
+		err = NULL;
+	}
+
+	g_key_file_free(key_file);
+}
+
+static void input_device_store_props(struct input_device *idev)
+{
+	char filename[PATH_MAX + 1];
+	GKeyFile *key_file;
+	char *str;
+	gsize length = 0;
+
+	input_device_config_filename(idev, filename, PATH_MAX + 1);
+
+	key_file = g_key_file_new();
+	if (!key_file)
+		return;
+	g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+	g_key_file_set_boolean(key_file, "Input",
+		"NormallyConnectable", idev->normally_connectable);
+	g_key_file_set_boolean(key_file, "Input",
+		"ReconnectInitiate", idev->reconnect_initiate);
+
+	create_file(filename, S_IRUSR | S_IWUSR);
+
+	str = g_key_file_to_data(key_file, &length, NULL);
+	g_file_set_contents(filename, str, length, NULL);
+	g_free(str);
+
+	g_key_file_free(key_file);
+}
+
 static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
 {
 	struct input_device *idev = data;
@@ -676,7 +755,7 @@ int input_device_disconnect(struct btd_device *dev, struct btd_profile *profile)
 
 static struct input_device *input_device_new(struct btd_device *device,
 				const char *path, const uint32_t handle,
-				gboolean disable_sdp)
+				bool disable_sdp)
 {
 	struct btd_adapter *adapter = device_get_adapter(device);
 	struct input_device *idev;
@@ -694,6 +773,8 @@ static struct input_device *input_device_new(struct btd_device *device,
 	if (strlen(name) > 0)
 		idev->name = g_strdup(name);
 
+	input_device_load_props(idev);
+
 	return idev;
 }
 
@@ -706,6 +787,76 @@ static gboolean is_device_sdp_disable(const sdp_record_t *rec)
 	return data && data->val.uint8;
 }
 
+static void update_hid_props(struct input_device *idev,
+		gboolean normally_connectable, gboolean reconnect_initiate)
+{
+	gboolean update_nc, update_ri;
+	DBusConnection *dbus_conn = btd_get_dbus_connection();
+
+	update_nc = idev->normally_connectable != normally_connectable;
+	idev->normally_connectable = normally_connectable;
+	if (update_nc)
+		g_dbus_emit_property_changed(dbus_conn, idev->path,
+				DEVICE_INTERFACE, "HIDNormallyConnectable");
+
+	update_ri = idev->reconnect_initiate != reconnect_initiate;
+	idev->reconnect_initiate = reconnect_initiate;
+	if (update_ri)
+		g_dbus_emit_property_changed(dbus_conn, idev->path,
+				DEVICE_INTERFACE, "HIDReconnectInitiate");
+
+	if (update_nc || update_ri)
+		input_device_store_props(idev);
+}
+
+static void extract_hid_props(struct input_device *idev,
+							const sdp_record_t *rec)
+{
+	/* Extract HID connectability */
+	bool hid_nc, hid_ri;
+	sdp_data_t *pdlist;
+
+	/* HIDNormallyConnectable is optional and assumed FALSE
+	* if not present. */
+	pdlist = sdp_data_get(rec, SDP_ATTR_HID_NORMALLY_CONNECTABLE);
+	hid_nc = pdlist ? pdlist->val.uint8 : FALSE;
+
+	pdlist = sdp_data_get(rec, SDP_ATTR_HID_RECONNECT_INITIATE);
+	hid_ri = pdlist ? pdlist->val.uint8 : TRUE;
+
+	update_hid_props(idev, hid_nc, hid_ri);
+}
+
+static gboolean property_get_normally_connectable(
+					const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct input_device *idev = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN,
+						&idev->normally_connectable);
+
+	return TRUE;
+}
+
+static gboolean property_get_reconnect_initiate(
+					const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct input_device *idev = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN,
+						&idev->reconnect_initiate);
+
+	return TRUE;
+}
+
+static const GDBusPropertyTable input_properties[] = {
+	{ "NormallyConnectable", "b", property_get_normally_connectable },
+	{ "ReconnectInitiate", "b", property_get_reconnect_initiate },
+	{ }
+};
+
 int input_device_register(struct btd_device *device,
 					const char *path, const char *uuid,
 					const sdp_record_t *rec, int timeout)
@@ -723,6 +874,18 @@ int input_device_register(struct btd_device *device,
 	if (!idev)
 		return -EINVAL;
 
+	if (g_dbus_register_interface(btd_get_dbus_connection(),
+					idev->path, INPUT_INTERFACE,
+					NULL, NULL,
+					input_properties, idev,
+					NULL) == FALSE) {
+		error("Unable to register %s interface", INPUT_INTERFACE);
+		input_device_free(idev);
+		return -EINVAL;
+	}
+
+	extract_hid_props(idev, rec);
+
 	idev->timeout = timeout;
 	idev->uuid = g_strdup(uuid);
 
@@ -761,6 +924,9 @@ int input_device_unregister(const char *path, const char *uuid)
 		return -EBUSY;
 	}
 
+	g_dbus_unregister_interface(btd_get_dbus_connection(),
+						idev->path, INPUT_INTERFACE);
+
 	devices = g_slist_remove(devices, idev);
 	input_device_free(idev);
 
-- 
1.8.1.3

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