[PATCH BlueZ 01/13] hog: HID I/O driver

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

 



uHID is HID I/O driver that makes possible to implement HID I/O drivers in
user-space. It works similar to the uinput but it is initialized with a HID
descriptor and deals with raw HID reports.

This commit uses uHID to create a HID device for the remote HoG device and
to tranfers HID reports to HID subsystem.
---
 acinclude.m4                |    9 ++++-
 configure.ac                |    2 +
 profiles/input/hog_device.c |   89 ++++++++++++++++++++++++++++++++++---------
 profiles/input/main.c       |    2 +
 profiles/input/manager.c    |    2 +
 5 files changed, 84 insertions(+), 20 deletions(-)

diff --git a/acinclude.m4 b/acinclude.m4
index 39b0a18..e04cbe3 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -161,6 +161,13 @@ AC_DEFUN([AC_PATH_OUI], [
 	AC_DEFINE_UNQUOTED(OUIFILE, ["$ac_with_ouifile"], [Define the OUI file path])
 ])
 
+AC_DEFUN([AC_PATH_UHID], [
+	AC_CHECK_HEADERS(linux/uhid.h,
+		uhid_found=yes,
+		uhid_found=no
+	)
+])
+
 AC_DEFUN([AC_ARG_BLUEZ], [
 	debug_enable=no
 	optimization_enable=yes
@@ -342,5 +349,5 @@ AC_DEFUN([AC_ARG_BLUEZ], [
 	AM_CONDITIONAL(DBUSOOBPLUGIN, test "${dbusoob_enable}" = "yes")
 	AM_CONDITIONAL(WIIMOTEPLUGIN, test "${wiimote_enable}" = "yes")
 	AM_CONDITIONAL(GATTMODULES, test "${gatt_enable}" = "yes")
-	AM_CONDITIONAL(HOGPLUGIN, test "${gatt_enable}" = "yes" && test "${input_enable}" = "yes")
+	AM_CONDITIONAL(HOGPLUGIN, test "${gatt_enable}" = "yes" && test "${input_enable}" = "yes" && test "${uhid_found}" = "yes")
 ])
diff --git a/configure.ac b/configure.ac
index 7f331ae..150171f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -39,6 +39,7 @@ AC_CHECK_HEADER([sys/inotify.h],
 		[AC_DEFINE([HAVE_SYS_INOTIFY_H], 1,
 			[Define to 1 if you have <sys/inotify.h>.])],
 			[AC_MSG_ERROR(inotify headers are required and missing)])
+
 AC_PATH_DBUS
 AC_PATH_GLIB
 AC_PATH_GSTREAMER
@@ -48,6 +49,7 @@ AC_PATH_SNDFILE
 AC_PATH_OUI
 AC_PATH_READLINE
 AC_PATH_CHECK
+AC_PATH_UHID
 
 AC_ARG_BLUEZ
 
diff --git a/profiles/input/hog_device.c b/profiles/input/hog_device.c
index 8e5e758..5dd9cf1 100644
--- a/profiles/input/hog_device.c
+++ b/profiles/input/hog_device.c
@@ -29,6 +29,10 @@
 #include <stdlib.h>
 #include <errno.h>
 #include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <linux/uhid.h>
 
 #include <bluetooth/bluetooth.h>
 #include <bluetooth/uuid.h>
@@ -49,6 +53,7 @@
 
 #define HOG_REPORT_MAP_UUID	0x2A4B
 #define HOG_REPORT_UUID		0x2A4D
+#define UHID_DEVICE_FILE	"/dev/uhid"
 
 #define HOG_REPORT_MAP_MAX_SIZE        512
 
@@ -64,21 +69,32 @@ struct hog_device {
 	guint			report_cb_id;
 	struct gatt_primary	*hog_primary;
 	GSList			*reports;
+	int			uhid_fd;
 };
 
 static GSList *devices = NULL;
 
 static void report_value_cb(const uint8_t *pdu, uint16_t len, gpointer user_data)
 {
-	uint16_t handle;
+	struct hog_device *hogdev = user_data;
+	struct uhid_event ev;
+	uint16_t report_size = len - 3;
 
 	if (len < 3) { /* 1-byte opcode + 2-byte handle */
 		error("Malformed ATT notification");
 		return;
 	}
 
-	handle = att_get_u16(&pdu[1]);
-	DBG("Report notification on handle 0x%04x", handle);
+	memset(&ev, 0, sizeof(ev));
+	ev.type = UHID_INPUT;
+	ev.u.input.size = MIN(report_size, UHID_DATA_MAX);
+	memcpy(ev.u.input.data, &pdu[3], MIN(report_size, UHID_DATA_MAX));
+
+	if (write(hogdev->uhid_fd, &ev, sizeof(ev)) < 0)
+		error("uHID write failed: %s", strerror(errno));
+	else
+		DBG("Report from HoG device %s written to uHID fd %d",
+						hogdev->path, hogdev->uhid_fd);
 }
 
 static void report_ccc_written_cb(guint8 status, const guint8 *pdu,
@@ -157,7 +173,9 @@ static void discover_descriptor(GAttrib *attrib, struct gatt_char *chr,
 static void report_map_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
 							gpointer user_data)
 {
+	struct hog_device *hogdev = user_data;
 	uint8_t value[HOG_REPORT_MAP_MAX_SIZE];
+	struct uhid_event ev;
 	ssize_t vlen;
 	int i;
 
@@ -179,6 +197,22 @@ static void report_map_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
 		else
 			DBG("\t %02x %02x", value[i], value[i + 1]);
 	}
+
+	/* create uHID device */
+	memset(&ev, 0, sizeof(ev));
+	ev.type = UHID_CREATE;
+	/* TODO: get info from DIS */
+	strcpy((char *)ev.u.create.name, "bluez-hog-device");
+	ev.u.create.vendor = 0xBEBA;
+	ev.u.create.product = 0xCAFE;
+	ev.u.create.version = 0;
+	ev.u.create.country = 0;
+	ev.u.create.bus = BUS_BLUETOOTH;
+	ev.u.create.rd_data = value;
+	ev.u.create.rd_size = vlen;
+
+	if (write(hogdev->uhid_fd, &ev, sizeof(ev)) < 0)
+		error("Failed to create uHID device: %s", strerror(errno));
 }
 
 static void char_discovered_cb(GSList *chars, guint8 status, gpointer user_data)
@@ -239,6 +273,12 @@ static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
 static void attio_disconnected_cb(gpointer user_data)
 {
 	struct hog_device *hogdev = user_data;
+	struct uhid_event ev;
+
+	memset(&ev, 0, sizeof(ev));
+	ev.type = UHID_DESTROY;
+	if (write(hogdev->uhid_fd, &ev, sizeof(ev)) < 0)
+		error("Failed to destroy uHID device: %s", strerror(errno));
 
 	g_attrib_unregister(hogdev->attrib, hogdev->report_cb_id);
 	hogdev->report_cb_id = 0;
@@ -293,6 +333,22 @@ static struct gatt_primary *load_hog_primary(struct btd_device *device)
 	return (l ? l->data : NULL);
 }
 
+static void report_free(void *data)
+{
+	struct report *report = data;
+	g_free(report->decl);
+	g_free(report);
+}
+
+static void hog_device_free(struct hog_device *hogdev)
+{
+	btd_device_unref(hogdev->device);
+	g_slist_free_full(hogdev->reports, report_free);
+	g_free(hogdev->path);
+	g_free(hogdev->hog_primary);
+	g_free(hogdev);
+}
+
 int hog_device_register(struct btd_device *device, const char *path)
 {
 	struct hog_device *hogdev;
@@ -310,6 +366,13 @@ int hog_device_register(struct btd_device *device, const char *path)
 	if (!hogdev)
 		return -ENOMEM;
 
+	hogdev->uhid_fd = open(UHID_DEVICE_FILE, O_RDWR | O_CLOEXEC);
+	if (hogdev->uhid_fd < 0) {
+		error("Failed to open uHID device: %s", strerror(errno));
+		hog_device_free(hogdev);
+		return -errno;
+	}
+
 	hogdev->hog_primary = g_memdup(prim, sizeof(*prim));
 
 	hogdev->attioid = btd_device_add_attio_callback(device,
@@ -323,22 +386,6 @@ int hog_device_register(struct btd_device *device, const char *path)
 	return 0;
 }
 
-static void report_free(void *data)
-{
-	struct report *report = data;
-	g_free(report->decl);
-	g_free(report);
-}
-
-static void hog_device_free(struct hog_device *hogdev)
-{
-	btd_device_unref(hogdev->device);
-	g_slist_free_full(hogdev->reports, report_free);
-	g_free(hogdev->path);
-	g_free(hogdev->hog_primary);
-	g_free(hogdev);
-}
-
 int hog_device_unregister(const char *path)
 {
 	struct hog_device *hogdev;
@@ -348,6 +395,10 @@ int hog_device_unregister(const char *path)
 		return -EINVAL;
 
 	btd_device_remove_attio_callback(hogdev->device, hogdev->attioid);
+
+	close(hogdev->uhid_fd);
+	hogdev->uhid_fd = -1;
+
 	devices = g_slist_remove(devices, hogdev);
 	hog_device_free(hogdev);
 
diff --git a/profiles/input/main.c b/profiles/input/main.c
index 722bc49..d1623ec 100644
--- a/profiles/input/main.c
+++ b/profiles/input/main.c
@@ -86,6 +86,7 @@ static void input_exit(void)
 BLUETOOTH_PLUGIN_DEFINE(input, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
 							input_init, input_exit)
 
+#ifdef HAVE_LINUX_UHID_H
 static int hog_init(void)
 {
 	if (!main_opts.gatt_enabled) {
@@ -106,3 +107,4 @@ static void hog_exit(void)
 
 BLUETOOTH_PLUGIN_DEFINE(hog, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
 							hog_init, hog_exit)
+#endif
diff --git a/profiles/input/manager.c b/profiles/input/manager.c
index 928a2f5..696569f 100644
--- a/profiles/input/manager.c
+++ b/profiles/input/manager.c
@@ -194,6 +194,7 @@ void input_manager_exit(void)
 	connection = NULL;
 }
 
+#ifdef HAVE_LINUX_UHID_H
 static int hog_device_probe(struct btd_device *device, GSList *uuids)
 {
 	const char *path = device_get_path(device);
@@ -228,3 +229,4 @@ void hog_manager_exit(void)
 {
 	btd_unregister_device_driver(&hog_driver);
 }
+#endif
-- 
1.7.10.4

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