[RFC generichid 3/5]

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

 



Adds connect_cb, initiate_keyboard and supporting functions.

 input/generichid.c |  402 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 402 insertions(+), 0 deletions(-)

diff --git a/input/generichid.c b/input/generichid.c
index ec8283d..3a5f073 100644
--- a/input/generichid.c
+++ b/input/generichid.c
@@ -348,18 +348,307 @@ static int sdp_keyboard_service(struct
adapter_data *adapt)
 }


+static void initiate_keyboard(struct keyboard_state *keyboard)
+{
+	keyboard->value[0] = 0xa1;
+	keyboard->value[1] = 0x01;
+
+	memset(&(keyboard->value[2]), 0, HIDP_KEYB_SIZE - 2);
+
+	/*
+	*	first 4 bytes in value are constant
+	*	keys start at index 4
+	*	last_value = 3 means no keys pressed
+	*/
+	keyboard->last_value = 3;
+}
+
+
 static DBusMessage *send_event(DBusConnection *conn,
 		DBusMessage *msg, void *data)
 {
 	return btd_error_failed(msg, "Invalid profile mode");
 }

+static gboolean set_protocol_listener(GIOChannel *chan, GIOCondition condition,
+					gpointer data)
+{
+    unsigned char b;
+    unsigned char ok;
+	struct device_data *dev = data;
+    int fd;
+    int outfd;
+    int err;
+    b = 0;
+    ok = 0;
+
+    fd = g_io_channel_unix_get_fd(chan);
+    err = read(fd, &b, 1);
+    if (err < 0)
+        error("Error %d: failed to read set_protocol/set_idle", err);
+    if ((b == 0x71) || (b == 0x90)) {
+        // set_protocol(report) or set_idle
+        outfd = g_io_channel_unix_get_fd(dev->intr);
+        err = write(outfd, &ok, 1);
+        if (err < 0)
+            error("Error %d: failed to acknowledge
set_protocol/set_idle", err);
+    } else {
+        btd_debug("possibly discarding important protocol traffic %x", b);
+    }
+	return TRUE;
+}
+
+
+static gboolean channel_listener(GIOChannel *chan, GIOCondition condition,
+					gpointer data)
+{
+	struct device_data *dev = data;
+
+	if (dev->intr != NULL) {
+		g_io_channel_unref(dev->intr);
+		dev->intr = NULL;
+	}
+
+	if (dev->ctrl != NULL) {
+		g_io_channel_unref(dev->ctrl);
+		dev->ctrl = NULL;
+	}
+
+	g_dbus_emit_signal(connection,  dev->input_path,
+				GENERIC_INPUT_DEVICE, "Disconnected",
+				DBUS_TYPE_INVALID);
+    btd_debug("Channel listener");
+	return FALSE;
+}
+
+
+static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err,
+					void *data)
+{
+	unsigned int w;
+	func_ptr reg_interface;
+	struct user_data *info = data;
+	struct adapter_data *adapt = info->adapt;
+	struct device_data *dev = adapt->dev;
+
+	if (conn_err) {
+		error("%s", conn_err->message);
+		goto failed;
+	}
+
+	/* Connect */
+	if (info->func != NULL) {
+		reg_interface = info->func;
+		btd_debug("Registering device");
+
+		if ((*reg_interface)(adapt) < 0)
+			goto failed;
+
+	/* Reconnect */
+	} else {
+		g_dbus_emit_signal(connection,  dev->input_path,
+					GENERIC_INPUT_DEVICE, "Reconnected",
+					DBUS_TYPE_INVALID);
+	}
+
+	adapt->pending = 0;
+
+	w = g_io_add_watch(dev->intr, G_IO_HUP | G_IO_ERR,
+				channel_listener, dev);
+	dev->intr_watch = w;
+
+	g_free(info);
+
+	return;
+
+failed:
+	g_free(info);
+	info = NULL;
+	g_io_channel_unref(dev->intr);
+	dev->intr = NULL;
+
+	if (dev->ctrl != NULL) {
+		g_io_channel_unref(dev->ctrl);
+		dev->ctrl = NULL;
+	}
+}
+
+
+
+static void control_connect_cb(GIOChannel *chan, GError *conn_err,
+					void *data)
+{
+	GIOChannel *io;
+	GError *err;
+	bdaddr_t src;
+	struct user_data *info = data;
+	struct adapter_data *adapt = info->adapt;
+	struct device_data *dev = adapt->dev;
+
+	if (conn_err) {
+		error("%s", conn_err->message);
+		goto failed;
+	}
+
+	adapter_get_address(adapt->adapter, &src);
+
+	io = bt_io_connect(BT_IO_L2CAP, interrupt_connect_cb, data,
+				NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR, &src,
+				BT_IO_OPT_DEST_BDADDR, &dev->dst,
+				BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR,
+				BT_IO_OPT_INVALID);
+
+	if (io == NULL) {
+		error("%s", err->message);
+		g_error_free(err);
+		goto failed;
+	}
+
+	dev->intr = io;
+
+	return;
+
+failed:
+	g_free(info);
+	info = NULL;
+	g_io_channel_unref(dev->ctrl);
+	dev->ctrl = NULL;
+}
+
+
+static DBusMessage *reconnect_device(DBusConnection *conn, DBusMessage *msg,
+					gpointer data)
+{
+}
+
+
+static DBusMessage *disconnect_device(DBusConnection *conn, DBusMessage *msg,
+					gpointer data)
+{
+}
+
+static const GDBusSignalTable ghid_input_device_signals[] = {
+	{ GDBUS_SIGNAL("Reconnected", NULL)	},
+	{ GDBUS_SIGNAL("Disconnected", NULL) },
+	{ }
+};
+
+static const GDBusMethodTable ghid_input_device_methods[] = {
+	{ GDBUS_METHOD("SendEvent", GDBUS_ARGS({"event", "yqy"}), NULL, send_event) },
+	{ GDBUS_METHOD("Reconnect", NULL, NULL, reconnect_device) },
+	{ GDBUS_METHOD("Disconnect", NULL, NULL, disconnect_device)	},
+	{}
+};
+
+
+static void generic_input_device_path(char *path, struct btd_adapter *adapter)
+{
+	char *adapt;
+	strcpy(path, "/org/bluez/input");
+
+	/* adding the adapter name */
+	adapt = strrchr(adapter_get_path(adapter), '/');
+	strcat(path, adapt);
+
+	strcat(path, "/device1");
+}
+
+static int register_input_device(struct adapter_data *adapt)
+{
+	struct device_data *dev = adapt->dev;
+
+	dev->input_path = g_try_new0(char, MAX_PATH_LENGTH);
+	if (dev->input_path == NULL)
+		return -ENOMEM;
+
+	generic_input_device_path(dev->input_path,
+					adapt->adapter);
+
+	initiate_keyboard(&dev->keyboard);
+
+	btd_debug("input path is %s", dev->input_path);
+
+	if (g_dbus_register_interface(connection,
+					dev->input_path,
+					GENERIC_INPUT_DEVICE,
+					ghid_input_device_methods,
+					ghid_input_device_signals, NULL,
+					adapt, NULL) == FALSE) {
+		error("D-Bus failed to register %s interface",
+				GENERIC_INPUT_DEVICE);
+		g_free(dev->input_path);
+		return -1;
+	}

+	g_dbus_emit_signal(connection,  adapter_get_path(adapt->adapter),
+				GENERIC_HID_INTERFACE, "IncomingConnection",
+				DBUS_TYPE_INVALID);
+
+	return 0;
+}

 static DBusMessage *connect_device(DBusConnection *conn, DBusMessage *msg,
 					gpointer data)
 {
+	GError *err = NULL;
+	GIOChannel *io;
+	DBusMessageIter iter;
+	char *str, addr[18];
+	bdaddr_t src;
+	struct adapter_data *adapt = data;
+	struct device_data *dev = adapt->dev;
+	struct user_data *info;
+
+	if (adapt->pending)
+		return btd_error_in_progress(msg);
+
+	if (dev->input_path != NULL)
+		return btd_error_already_connected(msg);
+
+		info = g_try_new(struct user_data, 1);
+
+	if (!dbus_message_iter_init(msg, &iter))
+			return btd_error_invalid_args(msg);
+
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+		return btd_error_invalid_args(msg);
+
+	if (info == NULL)
+		return btd_error_failed(msg, strerror(-ENOMEM));
+
+	info->adapt = adapt;
+	info->func = register_input_device;
+
+	dbus_message_iter_get_basic(&iter, &str);
+
+	strcpy(addr, str);
+	str2ba(addr, &(dev->dst));
+
+	btd_debug("Request connection to %s", addr);
+
+	adapter_get_address(adapt->adapter, &src);
+
+	io = bt_io_connect(BT_IO_L2CAP, control_connect_cb, info,
+				NULL, &err,
+				BT_IO_OPT_SOURCE_BDADDR, &src,
+				BT_IO_OPT_DEST_BDADDR, &(dev->dst),
+				BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL,
+				BT_IO_OPT_INVALID);
+
+	if (err != NULL)
+		error("%s", err->message);
+
+	if (io == NULL) {
+		if (info != NULL)
+			g_free(info);
+
+		return btd_error_failed(msg, "Failed to plug the device");
+	}
+
+	dev->ctrl = io;
 	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
 }


@@ -398,8 +687,121 @@ static void unregister_interface(const char *path)
 	g_dbus_unregister_interface(connection, path, GENERIC_HID_INTERFACE);
 }

+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer data)
+{
+	uint16_t psm;
+	bdaddr_t dst;
+	GError *gerr = NULL;
+	unsigned int w;
+	int ret;
+	struct adapter_data *adapt = data;
+	struct device_data *dev = adapt->dev;
+
+	if (err)
+		btd_debug("%s\n", err->message);
+
+	bt_io_get(chan, BT_IO_L2CAP, &gerr,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_PSM, &psm,
+			BT_IO_OPT_INVALID);
+
+	if (gerr) {
+		btd_debug("Error on PSM %d: %s\n", psm, gerr->message);
+		g_error_free(gerr);
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		return;
+	}
+
+	btd_debug("Accept on PSM %d\n", psm);
+
+	if (psm == 17) {
+		dev->ctrl = g_io_channel_ref(chan);
+		return;
+	}
+
+	dev->intr = g_io_channel_ref(chan);
+
+	if (dev->input_path == NULL) {
+
+		ret = register_input_device(adapt);
+
+		if (ret < 0)
+			goto failed;
+
+		bacpy(&dev->dst, &dst);
+
+	} else {
+		g_dbus_emit_signal(connection,  dev->input_path,
+					GENERIC_INPUT_DEVICE, "Reconnected",
+					DBUS_TYPE_INVALID);
+	}
+
+	w = g_io_add_watch(dev->intr, G_IO_HUP | G_IO_ERR,
+				channel_listener, dev);
+    g_io_add_watch(dev->ctrl, G_IO_IN, set_protocol_listener, dev);
+    btd_debug("Added watch in connect_cb to set_protocol_listener");
+	dev->intr_watch = w;
+	adapt->pending = 0;
+
+	return;
+
+failed:
+	if (dev->intr != NULL) {
+		g_io_channel_shutdown(dev->intr, TRUE, NULL);
+		g_io_channel_unref(dev->intr);
+		dev->intr = NULL;
+	}
+
+	if (dev->ctrl != NULL) {
+		g_io_channel_shutdown(dev->ctrl, TRUE, NULL);
+		g_io_channel_unref(dev->ctrl);
+		dev->ctrl = NULL;
+	}
+}
+
+
 static void confirm_event_cb(GIOChannel *chan, GError *err, gpointer data)
 {
+	uint16_t psm;
+	GError *gerr = NULL;
+	bdaddr_t dst;
+	struct adapter_data *adapt = data;
+	struct device_data *dev = adapt->dev;
+
+	if (err) {
+		error("%s\n", err->message);
+		return;
+	}
+
+	bt_io_get(chan, BT_IO_L2CAP, &gerr,
+			BT_IO_OPT_DEST_BDADDR, &dst,
+			BT_IO_OPT_PSM, &psm,
+			BT_IO_OPT_INVALID);
+	if (gerr) {
+		error("%s on PSM %d\n", gerr->message, psm);
+		g_error_free(gerr);
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		return;
+	}
+
+	if (dev->input_path != NULL &&
+			(bacmp(&(dev->dst), &dst) != 0 ||
+			dev->intr != NULL)) {
+
+		btd_debug("Incoming request blocked due to existing input device");
+		g_io_channel_shutdown(chan, TRUE, NULL);
+		return;
+	}
+
+	btd_debug("Incoming connection on PSM number %d", psm);
+
+	if (psm == 17)
+		adapt->pending = 1;
+
+	if (!bt_io_accept(chan, connect_cb, data, NULL, NULL))
+		btd_debug("Can not accept connection on psm %d", psm);
+
 }

 static int adapt_start(struct adapter_data *adapt)
--
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